Tue, 28 Dec 2010 15:52:36 -0800
6962318: Update copyright year
Reviewed-by: xdono
1 /*
2 * Copyright (c) 1998, 2010, 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 */
32 package com.sun.corba.se.impl.io;
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;
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;
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;
57 import java.util.Arrays;
58 import java.util.Comparator;
59 import java.util.Hashtable;
61 import com.sun.corba.se.impl.util.RepositoryId;
63 import org.omg.CORBA.ValueMember;
65 import sun.corba.Bridge;
67 /**
68 * A ObjectStreamClass describes a class that can be serialized to a stream
69 * or a class that was serialized to a stream. It contains the name
70 * and the serialVersionUID of the class.
71 * <br>
72 * The ObjectStreamClass for a specific class loaded in this Java VM can
73 * be found using the lookup method.
74 *
75 * @author Roger Riggs
76 * @since JDK1.1
77 */
78 public class ObjectStreamClass implements java.io.Serializable {
79 private static final boolean DEBUG_SVUID = false ;
81 public static final long kDefaultUID = -1;
83 private static Object noArgsList[] = {};
84 private static Class noTypesList[] = {};
86 private static Hashtable translatedFields;
88 /** true if represents enum type */
89 private boolean isEnum;
91 private static final Bridge bridge =
92 (Bridge)AccessController.doPrivileged(
93 new PrivilegedAction() {
94 public Object run() {
95 return Bridge.get() ;
96 }
97 }
98 ) ;
100 /** Find the descriptor for a class that can be serialized. Null
101 * is returned if the specified class does not implement
102 * java.io.Serializable or java.io.Externalizable.
103 */
104 static final ObjectStreamClass lookup(Class cl)
105 {
106 ObjectStreamClass desc = lookupInternal(cl);
107 if (desc.isSerializable() || desc.isExternalizable())
108 return desc;
109 return null;
110 }
112 /*
113 * Find the class descriptor for the specified class.
114 * Package access only so it can be called from ObjectIn/OutStream.
115 */
116 static ObjectStreamClass lookupInternal(Class cl)
117 {
118 /* Synchronize on the hashtable so no two threads will do
119 * this at the same time.
120 */
121 ObjectStreamClass desc = null;
122 synchronized (descriptorFor) {
123 /* Find the matching descriptor if it already known */
124 desc = findDescriptorFor(cl);
125 if (desc == null) {
126 /* Check if it's serializable */
127 boolean serializable = classSerializable.isAssignableFrom(cl);
129 /* If the class is only Serializable,
130 * lookup the descriptor for the superclass.
131 */
132 ObjectStreamClass superdesc = null;
133 if (serializable) {
134 Class superclass = cl.getSuperclass();
135 if (superclass != null)
136 superdesc = lookup(superclass);
137 }
139 /* Check if its' externalizable.
140 * If it's Externalizable, clear the serializable flag.
141 * Only one or the other may be set in the protocol.
142 */
143 boolean externalizable = false;
144 if (serializable) {
145 externalizable =
146 ((superdesc != null) && superdesc.isExternalizable()) ||
147 classExternalizable.isAssignableFrom(cl);
148 if (externalizable) {
149 serializable = false;
150 }
151 }
153 /* Create a new version descriptor,
154 * it put itself in the known table.
155 */
156 desc = new ObjectStreamClass(cl, superdesc,
157 serializable, externalizable);
158 }
159 // Must always call init. See bug 4488137. This code was
160 // incorrectly changed to return immediately on a non-null
161 // cache result. That allowed threads to gain access to
162 // unintialized instances.
163 //
164 // History: Note, the following init() call was originally within
165 // the synchronization block, as it currently is now. Later, the
166 // init() call was moved outside the synchronization block, and
167 // the init() method used a private member variable lock, to
168 // avoid performance problems. See bug 4165204. But that lead to
169 // a deadlock situation, see bug 5104239. Hence, the init() method
170 // has now been moved back into the synchronization block. The
171 // right approach to solving these problems would be to rewrite
172 // this class, based on the latest java.io.ObjectStreamClass.
173 desc.init();
174 }
175 return desc;
176 }
178 /**
179 * The name of the class described by this descriptor.
180 */
181 public final String getName() {
182 return name;
183 }
185 /**
186 * Return the serialVersionUID for this class.
187 * The serialVersionUID defines a set of classes all with the same name
188 * that have evolved from a common root class and agree to be serialized
189 * and deserialized using a common format.
190 */
191 public static final long getSerialVersionUID( java.lang.Class clazz) {
192 ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz );
193 if( theosc != null )
194 {
195 return theosc.getSerialVersionUID( );
196 }
197 return 0;
198 }
200 /**
201 * Return the serialVersionUID for this class.
202 * The serialVersionUID defines a set of classes all with the same name
203 * that have evolved from a common root class and agree to be serialized
204 * and deserialized using a common format.
205 */
206 public final long getSerialVersionUID() {
207 return suid;
208 }
210 /**
211 * Return the serialVersionUID string for this class.
212 * The serialVersionUID defines a set of classes all with the same name
213 * that have evolved from a common root class and agree to be serialized
214 * and deserialized using a common format.
215 */
216 public final String getSerialVersionUIDStr() {
217 if (suidStr == null)
218 suidStr = Long.toHexString(suid).toUpperCase();
219 return suidStr;
220 }
222 /**
223 * Return the actual (computed) serialVersionUID for this class.
224 */
225 public static final long getActualSerialVersionUID( java.lang.Class clazz )
226 {
227 ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz );
228 if( theosc != null )
229 {
230 return theosc.getActualSerialVersionUID( );
231 }
232 return 0;
233 }
235 /**
236 * Return the actual (computed) serialVersionUID for this class.
237 */
238 public final long getActualSerialVersionUID() {
239 return actualSuid;
240 }
242 /**
243 * Return the actual (computed) serialVersionUID for this class.
244 */
245 public final String getActualSerialVersionUIDStr() {
246 if (actualSuidStr == null)
247 actualSuidStr = Long.toHexString(actualSuid).toUpperCase();
248 return actualSuidStr;
249 }
251 /**
252 * Return the class in the local VM that this version is mapped to.
253 * Null is returned if there is no corresponding local class.
254 */
255 public final Class forClass() {
256 return ofClass;
257 }
259 /**
260 * Return an array of the fields of this serializable class.
261 * @return an array containing an element for each persistent
262 * field of this class. Returns an array of length zero if
263 * there are no fields.
264 * @since JDK1.2
265 */
266 public ObjectStreamField[] getFields() {
267 // Return a copy so the caller can't change the fields.
268 if (fields.length > 0) {
269 ObjectStreamField[] dup = new ObjectStreamField[fields.length];
270 System.arraycopy(fields, 0, dup, 0, fields.length);
271 return dup;
272 } else {
273 return fields;
274 }
275 }
277 public boolean hasField(ValueMember field)
278 {
279 try {
280 for (int i = 0; i < fields.length; i++) {
281 if (fields[i].getName().equals(field.name)) {
282 if (fields[i].getSignature().equals(
283 ValueUtility.getSignature(field)))
284 return true;
285 }
286 }
287 } catch (Exception exc) {
288 // Ignore this; all we want to do is return false
289 // Note that ValueUtility.getSignature can throw checked exceptions.
290 }
292 return false;
293 }
295 /* Avoid unnecessary allocations. */
296 final ObjectStreamField[] getFieldsNoCopy() {
297 return fields;
298 }
300 /**
301 * Get the field of this class by name.
302 * @return The ObjectStreamField object of the named field or null if there
303 * is no such named field.
304 */
305 public final ObjectStreamField getField(String name) {
306 /* Binary search of fields by name.
307 */
308 for (int i = fields.length-1; i >= 0; i--) {
309 if (name.equals(fields[i].getName())) {
310 return fields[i];
311 }
312 }
313 return null;
314 }
316 public Serializable writeReplace(Serializable value) {
317 if (writeReplaceObjectMethod != null) {
318 try {
319 return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList);
320 } catch(Throwable t) {
321 throw new RuntimeException(t);
322 }
323 }
324 else return value;
325 }
327 public Object readResolve(Object value) {
328 if (readResolveObjectMethod != null) {
329 try {
330 return readResolveObjectMethod.invoke(value,noArgsList);
331 } catch(Throwable t) {
332 throw new RuntimeException(t);
333 }
334 }
335 else return value;
336 }
338 /**
339 * Return a string describing this ObjectStreamClass.
340 */
341 public final String toString() {
342 StringBuffer sb = new StringBuffer();
344 sb.append(name);
345 sb.append(": static final long serialVersionUID = ");
346 sb.append(Long.toString(suid));
347 sb.append("L;");
348 return sb.toString();
349 }
351 /*
352 * Create a new ObjectStreamClass from a loaded class.
353 * Don't call this directly, call lookup instead.
354 */
355 private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc,
356 boolean serial, boolean extern)
357 {
358 ofClass = cl; /* created from this class */
360 if (Proxy.isProxyClass(cl)) {
361 forProxyClass = true;
362 }
364 name = cl.getName();
365 isEnum = Enum.class.isAssignableFrom(cl);
366 superclass = superdesc;
367 serializable = serial;
368 if (!forProxyClass) {
369 // proxy classes are never externalizable
370 externalizable = extern;
371 }
373 /*
374 * Enter this class in the table of known descriptors.
375 * Otherwise, when the fields are read it may recurse
376 * trying to find the descriptor for itself.
377 */
378 insertDescriptorFor(this);
380 /*
381 * The remainder of initialization occurs in init(), which is called
382 * after the lock on the global class descriptor table has been
383 * released.
384 */
385 }
387 /*
388 * Initialize class descriptor. This method is only invoked on class
389 * descriptors created via calls to lookupInternal(). This method is kept
390 * separate from the ObjectStreamClass constructor so that lookupInternal
391 * does not have to hold onto a global class descriptor table lock while the
392 * class descriptor is being initialized (see bug 4165204).
393 */
396 private void init() {
397 synchronized (lock) {
399 // See description at definition of initialized.
400 if (initialized)
401 return;
403 final Class cl = ofClass;
405 if (!serializable ||
406 externalizable ||
407 forProxyClass ||
408 name.equals("java.lang.String") ||
409 isEnum) {
410 fields = NO_FIELDS;
411 } else if (serializable) {
412 /* Ask for permission to override field access checks.
413 */
414 AccessController.doPrivileged(new PrivilegedAction() {
415 public Object run() {
416 /* Fill in the list of persistent fields.
417 * If it is declared, use the declared serialPersistentFields.
418 * Otherwise, extract the fields from the class itself.
419 */
420 try {
421 Field pf = cl.getDeclaredField("serialPersistentFields");
422 // serial bug 7; the serialPersistentFields were not
423 // being read and stored as Accessible bit was not set
424 pf.setAccessible(true);
425 // serial bug 7; need to find if the field is of type
426 // java.io.ObjectStreamField
427 java.io.ObjectStreamField[] f =
428 (java.io.ObjectStreamField[])pf.get(cl);
429 int mods = pf.getModifiers();
430 if ((Modifier.isPrivate(mods)) &&
431 (Modifier.isStatic(mods)) &&
432 (Modifier.isFinal(mods)))
433 {
434 fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl));
435 }
436 } catch (NoSuchFieldException e) {
437 fields = null;
438 } catch (IllegalAccessException e) {
439 fields = null;
440 } catch (IllegalArgumentException e) {
441 fields = null;
442 } catch (ClassCastException e) {
443 /* Thrown if a field serialPersistentField exists
444 * but it is not of type ObjectStreamField.
445 */
446 fields = null;
447 }
450 if (fields == null) {
451 /* Get all of the declared fields for this
452 * Class. setAccessible on all fields so they
453 * can be accessed later. Create a temporary
454 * ObjectStreamField array to hold each
455 * non-static, non-transient field. Then copy the
456 * temporary array into an array of the correct
457 * size once the number of fields is known.
458 */
459 Field[] actualfields = cl.getDeclaredFields();
461 int numFields = 0;
462 ObjectStreamField[] tempFields =
463 new ObjectStreamField[actualfields.length];
464 for (int i = 0; i < actualfields.length; i++) {
465 Field fld = actualfields[i] ;
466 int modifiers = fld.getModifiers();
467 if (!Modifier.isStatic(modifiers) &&
468 !Modifier.isTransient(modifiers)) {
469 fld.setAccessible(true) ;
470 tempFields[numFields++] = new ObjectStreamField(fld);
471 }
472 }
474 fields = new ObjectStreamField[numFields];
475 System.arraycopy(tempFields, 0, fields, 0, numFields);
477 } else {
478 // For each declared persistent field, look for an actual
479 // reflected Field. If there is one, make sure it's the correct
480 // type and cache it in the ObjectStreamClass for that field.
481 for (int j = fields.length-1; j >= 0; j--) {
482 try {
483 Field reflField = cl.getDeclaredField(fields[j].getName());
484 if (fields[j].getType() == reflField.getType()) {
485 reflField.setAccessible(true);
486 fields[j].setField(reflField);
487 }
488 } catch (NoSuchFieldException e) {
489 // Nothing to do
490 }
491 }
492 }
493 return null;
494 }
495 });
497 if (fields.length > 1)
498 Arrays.sort(fields);
500 /* Set up field data for use while writing using the API api. */
501 computeFieldInfo();
502 }
504 /* Get the serialVersionUID from the class.
505 * It uses the access override mechanism so make sure
506 * the field objects is only used here.
507 *
508 * NonSerializable classes have a serialVerisonUID of 0L.
509 */
510 if (isNonSerializable() || isEnum) {
511 suid = 0L;
512 } else {
513 // Lookup special Serializable members using reflection.
514 AccessController.doPrivileged(new PrivilegedAction() {
515 public Object run() {
516 if (forProxyClass) {
517 // proxy classes always have serialVersionUID of 0L
518 suid = 0L;
519 } else {
520 try {
521 final Field f = cl.getDeclaredField("serialVersionUID");
522 int mods = f.getModifiers();
523 // SerialBug 5: static final SUID should be read
524 if (Modifier.isStatic(mods) && Modifier.isFinal(mods) ) {
525 f.setAccessible(true);
526 suid = f.getLong(cl);
527 // SerialBug 2: should be computed after writeObject
528 // actualSuid = computeStructuralUID(cl);
529 } else {
530 suid = _computeSerialVersionUID(cl);
531 // SerialBug 2: should be computed after writeObject
532 // actualSuid = computeStructuralUID(cl);
533 }
534 } catch (NoSuchFieldException ex) {
535 suid = _computeSerialVersionUID(cl);
536 // SerialBug 2: should be computed after writeObject
537 // actualSuid = computeStructuralUID(cl);
538 } catch (IllegalAccessException ex) {
539 suid = _computeSerialVersionUID(cl);
540 }
541 }
543 writeReplaceObjectMethod = ObjectStreamClass.getInheritableMethod(cl,
544 "writeReplace", noTypesList, Object.class);
546 readResolveObjectMethod = ObjectStreamClass.getInheritableMethod(cl,
547 "readResolve", noTypesList, Object.class);
549 if (externalizable)
550 cons = getExternalizableConstructor(cl) ;
551 else
552 cons = getSerializableConstructor(cl) ;
554 if (serializable && !forProxyClass) {
555 /* Look for the writeObject method
556 * Set the accessible flag on it here. ObjectOutputStream
557 * will call it as necessary.
558 */
559 writeObjectMethod = getPrivateMethod( cl, "writeObject",
560 new Class[] { java.io.ObjectOutputStream.class }, Void.TYPE ) ;
561 readObjectMethod = getPrivateMethod( cl, "readObject",
562 new Class[] { java.io.ObjectInputStream.class }, Void.TYPE ) ;
563 }
564 return null;
565 }
566 });
567 }
569 // This call depends on a lot of information computed above!
570 actualSuid = ObjectStreamClass.computeStructuralUID(this, cl);
572 // If we have a write object method, precompute the
573 // RMI-IIOP stream format version 2 optional data
574 // repository ID.
575 if (hasWriteObject())
576 rmiiiopOptionalDataRepId = computeRMIIIOPOptionalDataRepId();
578 // This must be done last.
579 initialized = true;
580 }
581 }
583 /**
584 * Returns non-static private method with given signature defined by given
585 * class, or null if none found. Access checks are disabled on the
586 * returned method (if any).
587 */
588 private static Method getPrivateMethod(Class cl, String name,
589 Class[] argTypes,
590 Class returnType)
591 {
592 try {
593 Method meth = cl.getDeclaredMethod(name, argTypes);
594 meth.setAccessible(true);
595 int mods = meth.getModifiers();
596 return ((meth.getReturnType() == returnType) &&
597 ((mods & Modifier.STATIC) == 0) &&
598 ((mods & Modifier.PRIVATE) != 0)) ? meth : null;
599 } catch (NoSuchMethodException ex) {
600 return null;
601 }
602 }
604 // Specific to RMI-IIOP
605 /**
606 * Java to IDL ptc-02-01-12 1.5.1
607 *
608 * "The rep_id string passed to the start_value method must be
609 * 'RMI:org.omg.custom.class:hashcode:suid' where class is the
610 * fully-qualified name of the class whose writeObject method
611 * is being invoked and hashcode and suid are the class's hashcode
612 * and SUID."
613 */
614 private String computeRMIIIOPOptionalDataRepId() {
616 StringBuffer sbuf = new StringBuffer("RMI:org.omg.custom.");
617 sbuf.append(RepositoryId.convertToISOLatin1(this.getName()));
618 sbuf.append(':');
619 sbuf.append(this.getActualSerialVersionUIDStr());
620 sbuf.append(':');
621 sbuf.append(this.getSerialVersionUIDStr());
623 return sbuf.toString();
624 }
626 /**
627 * This will return null if there is no writeObject method.
628 */
629 public final String getRMIIIOPOptionalDataRepId() {
630 return rmiiiopOptionalDataRepId;
631 }
633 /*
634 * Create an empty ObjectStreamClass for a class about to be read.
635 * This is separate from read so ObjectInputStream can assign the
636 * wire handle early, before any nested ObjectStreamClass might
637 * be read.
638 */
639 ObjectStreamClass(String n, long s) {
640 name = n;
641 suid = s;
642 superclass = null;
643 }
645 private static Object[] translateFields(Object objs[])
646 throws NoSuchFieldException {
647 try{
648 java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs;
649 Object translation[] = null;
651 if (translatedFields == null)
652 translatedFields = new Hashtable();
654 translation = (Object[])translatedFields.get(fields);
656 if (translation != null)
657 return translation;
658 else {
659 Class osfClass = Class.forName("com.sun.corba.se.impl.io.ObjectStreamField");
660 translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length);
661 Object arg[] = new Object[2];
662 Class types[] = {String.class, Class.class};
663 Constructor constructor = osfClass.getDeclaredConstructor(types);
664 for (int i = fields.length -1; i >= 0; i--){
665 arg[0] = fields[i].getName();
666 arg[1] = fields[i].getType();
668 translation[i] = constructor.newInstance(arg);
669 }
670 translatedFields.put(fields, translation);
672 }
674 return (Object[])translation;
675 }
676 catch(Throwable t){
677 NoSuchFieldException nsfe = new NoSuchFieldException();
678 nsfe.initCause( t ) ;
679 throw nsfe ;
680 }
681 }
683 /*
684 * Set the class this version descriptor matches.
685 * The base class name and serializable hash must match.
686 * Fill in the reflected Fields that will be used
687 * for reading.
688 */
689 final void setClass(Class cl) throws InvalidClassException {
691 if (cl == null) {
692 localClassDesc = null;
693 ofClass = null;
694 computeFieldInfo();
695 return;
696 }
698 localClassDesc = lookupInternal(cl);
699 if (localClassDesc == null)
700 // XXX I18N, logging needed
701 throw new InvalidClassException(cl.getName(),
702 "Local class not compatible");
703 if (suid != localClassDesc.suid) {
705 /* Check for exceptional cases that allow mismatched suid. */
707 /* Allow adding Serializable or Externalizable
708 * to a later release of the class.
709 */
710 boolean addedSerialOrExtern =
711 isNonSerializable() || localClassDesc.isNonSerializable();
713 /* Disregard the serialVersionUID of an array
714 * when name and cl.Name differ. If resolveClass() returns
715 * an array with a different package name,
716 * the serialVersionUIDs will not match since the fully
717 * qualified array class is used in the
718 * computation of the array's serialVersionUID. There is
719 * no way to set a permanent serialVersionUID for an array type.
720 */
722 boolean arraySUID = (cl.isArray() && ! cl.getName().equals(name));
724 if (! arraySUID && ! addedSerialOrExtern ) {
725 // XXX I18N, logging needed
726 throw new InvalidClassException(cl.getName(),
727 "Local class not compatible:" +
728 " stream classdesc serialVersionUID=" + suid +
729 " local class serialVersionUID=" + localClassDesc.suid);
730 }
731 }
733 /* compare the class names, stripping off package names. */
734 if (! compareClassNames(name, cl.getName(), '.'))
735 // XXX I18N, logging needed
736 throw new InvalidClassException(cl.getName(),
737 "Incompatible local class name. " +
738 "Expected class name compatible with " +
739 name);
741 /*
742 * Test that both implement either serializable or externalizable.
743 */
745 // The next check is more generic, since it covers the
746 // Proxy case, the JDK 1.3 serialization code has
747 // both checks
748 //if ((serializable && localClassDesc.externalizable) ||
749 // (externalizable && localClassDesc.serializable))
750 // throw new InvalidClassException(localCl.getName(),
751 // "Serializable is incompatible with Externalizable");
753 if ((serializable != localClassDesc.serializable) ||
754 (externalizable != localClassDesc.externalizable) ||
755 (!serializable && !externalizable))
757 // XXX I18N, logging needed
758 throw new InvalidClassException(cl.getName(),
759 "Serialization incompatible with Externalization");
761 /* Set up the reflected Fields in the class where the value of each
762 * field in this descriptor should be stored.
763 * Each field in this ObjectStreamClass (the source) is located (by
764 * name) in the ObjectStreamClass of the class(the destination).
765 * In the usual (non-versioned case) the field is in both
766 * descriptors and the types match, so the reflected Field is copied.
767 * If the type does not match, a InvalidClass exception is thrown.
768 * If the field is not present in the class, the reflected Field
769 * remains null so the field will be read but discarded.
770 * If extra fields are present in the class they are ignored. Their
771 * values will be set to the default value by the object allocator.
772 * Both the src and dest field list are sorted by type and name.
773 */
775 ObjectStreamField[] destfield =
776 (ObjectStreamField[])localClassDesc.fields;
777 ObjectStreamField[] srcfield =
778 (ObjectStreamField[])fields;
780 int j = 0;
781 nextsrc:
782 for (int i = 0; i < srcfield.length; i++ ) {
783 /* Find this field in the dest*/
784 for (int k = j; k < destfield.length; k++) {
785 if (srcfield[i].getName().equals(destfield[k].getName())) {
786 /* found match */
787 if (srcfield[i].isPrimitive() &&
788 !srcfield[i].typeEquals(destfield[k])) {
789 // XXX I18N, logging needed
790 throw new InvalidClassException(cl.getName(),
791 "The type of field " +
792 srcfield[i].getName() +
793 " of class " + name +
794 " is incompatible.");
795 }
797 /* Skip over any fields in the dest that are not in the src */
798 j = k;
800 srcfield[i].setField(destfield[j].getField());
801 // go on to the next source field
802 continue nextsrc;
803 }
804 }
805 }
807 /* Set up field data for use while reading from the input stream. */
808 computeFieldInfo();
810 /* Remember the class this represents */
811 ofClass = cl;
813 /* get the cache of these methods from the local class
814 * implementation.
815 */
816 readObjectMethod = localClassDesc.readObjectMethod;
817 readResolveObjectMethod = localClassDesc.readResolveObjectMethod;
818 }
820 /* Compare the base class names of streamName and localName.
821 *
822 * @return Return true iff the base class name compare.
823 * @parameter streamName Fully qualified class name.
824 * @parameter localName Fully qualified class name.
825 * @parameter pkgSeparator class names use either '.' or '/'.
826 *
827 * Only compare base class name to allow package renaming.
828 */
829 static boolean compareClassNames(String streamName,
830 String localName,
831 char pkgSeparator) {
832 /* compare the class names, stripping off package names. */
833 int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
834 if (streamNameIndex < 0)
835 streamNameIndex = 0;
837 int localNameIndex = localName.lastIndexOf(pkgSeparator);
838 if (localNameIndex < 0)
839 localNameIndex = 0;
841 return streamName.regionMatches(false, streamNameIndex,
842 localName, localNameIndex,
843 streamName.length() - streamNameIndex);
844 }
846 /*
847 * Compare the types of two class descriptors.
848 * They match if they have the same class name and suid
849 */
850 final boolean typeEquals(ObjectStreamClass other) {
851 return (suid == other.suid) &&
852 compareClassNames(name, other.name, '.');
853 }
855 /*
856 * Return the superclass descriptor of this descriptor.
857 */
858 final void setSuperclass(ObjectStreamClass s) {
859 superclass = s;
860 }
862 /*
863 * Return the superclass descriptor of this descriptor.
864 */
865 final ObjectStreamClass getSuperclass() {
866 return superclass;
867 }
869 /**
870 * Return whether the class has a readObject method
871 */
872 final boolean hasReadObject() {
873 return readObjectMethod != null;
874 }
876 /*
877 * Return whether the class has a writeObject method
878 */
879 final boolean hasWriteObject() {
880 return writeObjectMethod != null ;
881 }
883 /**
884 * Returns when or not this class should be custom
885 * marshaled (use chunking). This should happen if
886 * it is Externalizable OR if it or
887 * any of its superclasses has a writeObject method,
888 */
889 final boolean isCustomMarshaled() {
890 return (hasWriteObject() || isExternalizable())
891 || (superclass != null && superclass.isCustomMarshaled());
892 }
894 /*
895 * Return true if all instances of 'this' Externalizable class
896 * are written in block-data mode from the stream that 'this' was read
897 * from. <p>
898 *
899 * In JDK 1.1, all Externalizable instances are not written
900 * in block-data mode.
901 * In JDK 1.2, all Externalizable instances, by default, are written
902 * in block-data mode and the Externalizable instance is terminated with
903 * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable
904 * instances.
905 *
906 * IMPLEMENTATION NOTE:
907 * This should have been a mode maintained per stream; however,
908 * for compatibility reasons, it was only possible to record
909 * this change per class. All Externalizable classes within
910 * a given stream should either have this mode enabled or
911 * disabled. This is enforced by not allowing the PROTOCOL_VERSION
912 * of a stream to he changed after any objects have been written.
913 *
914 * @see ObjectOutputStream#useProtocolVersion
915 * @see ObjectStreamConstants#PROTOCOL_VERSION_1
916 * @see ObjectStreamConstants#PROTOCOL_VERSION_2
917 *
918 * @since JDK 1.2
919 */
920 boolean hasExternalizableBlockDataMode() {
921 return hasExternalizableBlockData;
922 }
924 /**
925 * Creates a new instance of the represented class. If the class is
926 * externalizable, invokes its public no-arg constructor; otherwise, if the
927 * class is serializable, invokes the no-arg constructor of the first
928 * non-serializable superclass. Throws UnsupportedOperationException if
929 * this class descriptor is not associated with a class, if the associated
930 * class is non-serializable or if the appropriate no-arg constructor is
931 * inaccessible/unavailable.
932 */
933 Object newInstance()
934 throws InstantiationException, InvocationTargetException,
935 UnsupportedOperationException
936 {
937 if (cons != null) {
938 try {
939 return cons.newInstance(new Object[0]);
940 } catch (IllegalAccessException ex) {
941 // should not occur, as access checks have been suppressed
942 InternalError ie = new InternalError();
943 ie.initCause( ex ) ;
944 throw ie ;
945 }
946 } else {
947 throw new UnsupportedOperationException();
948 }
949 }
951 /**
952 * Returns public no-arg constructor of given class, or null if none found.
953 * Access checks are disabled on the returned constructor (if any), since
954 * the defining class may still be non-public.
955 */
956 private static Constructor getExternalizableConstructor(Class cl) {
957 try {
958 Constructor cons = cl.getDeclaredConstructor(new Class[0]);
959 cons.setAccessible(true);
960 return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
961 cons : null;
962 } catch (NoSuchMethodException ex) {
963 return null;
964 }
965 }
967 /**
968 * Returns subclass-accessible no-arg constructor of first non-serializable
969 * superclass, or null if none found. Access checks are disabled on the
970 * returned constructor (if any).
971 */
972 private static Constructor getSerializableConstructor(Class cl) {
973 Class initCl = cl;
974 while (Serializable.class.isAssignableFrom(initCl)) {
975 if ((initCl = initCl.getSuperclass()) == null) {
976 return null;
977 }
978 }
979 try {
980 Constructor cons = initCl.getDeclaredConstructor(new Class[0]);
981 int mods = cons.getModifiers();
982 if ((mods & Modifier.PRIVATE) != 0 ||
983 ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
984 !packageEquals(cl, initCl)))
985 {
986 return null;
987 }
988 cons = bridge.newConstructorForSerialization(cl, cons);
989 cons.setAccessible(true);
990 return cons;
991 } catch (NoSuchMethodException ex) {
992 return null;
993 }
994 }
996 /*
997 * Return the ObjectStreamClass of the local class this one is based on.
998 */
999 final ObjectStreamClass localClassDescriptor() {
1000 return localClassDesc;
1001 }
1003 /*
1004 * Get the Serializability of the class.
1005 */
1006 boolean isSerializable() {
1007 return serializable;
1008 }
1010 /*
1011 * Get the externalizability of the class.
1012 */
1013 boolean isExternalizable() {
1014 return externalizable;
1015 }
1017 boolean isNonSerializable() {
1018 return ! (externalizable || serializable);
1019 }
1021 /*
1022 * Calculate the size of the array needed to store primitive data and the
1023 * number of object references to read when reading from the input
1024 * stream.
1025 */
1026 private void computeFieldInfo() {
1027 primBytes = 0;
1028 objFields = 0;
1030 for (int i = 0; i < fields.length; i++ ) {
1031 switch (fields[i].getTypeCode()) {
1032 case 'B':
1033 case 'Z':
1034 primBytes += 1;
1035 break;
1036 case 'C':
1037 case 'S':
1038 primBytes += 2;
1039 break;
1041 case 'I':
1042 case 'F':
1043 primBytes += 4;
1044 break;
1045 case 'J':
1046 case 'D' :
1047 primBytes += 8;
1048 break;
1050 case 'L':
1051 case '[':
1052 objFields += 1;
1053 break;
1054 }
1055 }
1056 }
1058 private static void msg( String str )
1059 {
1060 System.out.println( str ) ;
1061 }
1063 /* JDK 1.5 has introduced some new modifier bits (such as SYNTHETIC)
1064 * that can affect the SVUID computation (see bug 4897937). These bits
1065 * must be ignored, as otherwise interoperability with ORBs in earlier
1066 * JDK versions can be compromised. I am adding these masks for this
1067 * purpose as discussed in the CCC for this bug (see http://ccc.sfbay/4897937).
1068 */
1070 public static final int CLASS_MASK = Modifier.PUBLIC | Modifier.FINAL |
1071 Modifier.INTERFACE | Modifier.ABSTRACT ;
1072 public static final int FIELD_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
1073 Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL |
1074 Modifier.TRANSIENT | Modifier.VOLATILE ;
1075 public static final int METHOD_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
1076 Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL |
1077 Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT |
1078 Modifier.STRICT ;
1080 /*
1081 * Compute a hash for the specified class. Incrementally add
1082 * items to the hash accumulating in the digest stream.
1083 * Fold the hash into a long. Use the SHA secure hash function.
1084 */
1085 private static long _computeSerialVersionUID(Class cl) {
1086 if (DEBUG_SVUID)
1087 msg( "Computing SerialVersionUID for " + cl ) ;
1088 ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
1090 long h = 0;
1091 try {
1092 MessageDigest md = MessageDigest.getInstance("SHA");
1093 DigestOutputStream mdo = new DigestOutputStream(devnull, md);
1094 DataOutputStream data = new DataOutputStream(mdo);
1096 if (DEBUG_SVUID)
1097 msg( "\twriteUTF( \"" + cl.getName() + "\" )" ) ;
1098 data.writeUTF(cl.getName());
1100 int classaccess = cl.getModifiers();
1101 classaccess &= (Modifier.PUBLIC | Modifier.FINAL |
1102 Modifier.INTERFACE | Modifier.ABSTRACT);
1104 /* Workaround for javac bug that only set ABSTRACT for
1105 * interfaces if the interface had some methods.
1106 * The ABSTRACT bit reflects that the number of methods > 0.
1107 * This is required so correct hashes can be computed
1108 * for existing class files.
1109 * Previously this hack was previously present in the VM.
1110 */
1111 Method[] method = cl.getDeclaredMethods();
1112 if ((classaccess & Modifier.INTERFACE) != 0) {
1113 classaccess &= (~Modifier.ABSTRACT);
1114 if (method.length > 0) {
1115 classaccess |= Modifier.ABSTRACT;
1116 }
1117 }
1119 // Mask out any post-1.4 attributes
1120 classaccess &= CLASS_MASK ;
1122 if (DEBUG_SVUID)
1123 msg( "\twriteInt( " + classaccess + " ) " ) ;
1124 data.writeInt(classaccess);
1126 /*
1127 * Get the list of interfaces supported,
1128 * Accumulate their names their names in Lexical order
1129 * and add them to the hash
1130 */
1131 if (!cl.isArray()) {
1132 /* In 1.2fcs, getInterfaces() was modified to return
1133 * {java.lang.Cloneable, java.io.Serializable} when
1134 * called on array classes. These values would upset
1135 * the computation of the hash, so we explicitly omit
1136 * them from its computation.
1137 */
1139 Class interfaces[] = cl.getInterfaces();
1140 Arrays.sort(interfaces, compareClassByName);
1142 for (int i = 0; i < interfaces.length; i++) {
1143 if (DEBUG_SVUID)
1144 msg( "\twriteUTF( \"" + interfaces[i].getName() + "\" ) " ) ;
1145 data.writeUTF(interfaces[i].getName());
1146 }
1147 }
1149 /* Sort the field names to get a deterministic order */
1150 Field[] field = cl.getDeclaredFields();
1151 Arrays.sort(field, compareMemberByName);
1153 for (int i = 0; i < field.length; i++) {
1154 Field f = field[i];
1156 /* Include in the hash all fields except those that are
1157 * private transient and private static.
1158 */
1159 int m = f.getModifiers();
1160 if (Modifier.isPrivate(m) &&
1161 (Modifier.isTransient(m) || Modifier.isStatic(m)))
1162 continue;
1164 if (DEBUG_SVUID)
1165 msg( "\twriteUTF( \"" + f.getName() + "\" ) " ) ;
1166 data.writeUTF(f.getName());
1168 // Mask out any post-1.4 bits
1169 m &= FIELD_MASK ;
1171 if (DEBUG_SVUID)
1172 msg( "\twriteInt( " + m + " ) " ) ;
1173 data.writeInt(m);
1175 if (DEBUG_SVUID)
1176 msg( "\twriteUTF( \"" + getSignature(f.getType()) + "\" ) " ) ;
1177 data.writeUTF(getSignature(f.getType()));
1178 }
1180 if (hasStaticInitializer(cl)) {
1181 if (DEBUG_SVUID)
1182 msg( "\twriteUTF( \"<clinit>\" ) " ) ;
1183 data.writeUTF("<clinit>");
1185 if (DEBUG_SVUID)
1186 msg( "\twriteInt( " + Modifier.STATIC + " )" ) ;
1187 data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have
1189 if (DEBUG_SVUID)
1190 msg( "\twriteUTF( \"()V\" )" ) ;
1191 data.writeUTF("()V");
1192 }
1194 /*
1195 * Get the list of constructors including name and signature
1196 * Sort lexically, add all except the private constructors
1197 * to the hash with their access flags
1198 */
1200 MethodSignature[] constructors =
1201 MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors());
1202 for (int i = 0; i < constructors.length; i++) {
1203 MethodSignature c = constructors[i];
1204 String mname = "<init>";
1205 String desc = c.signature;
1206 desc = desc.replace('/', '.');
1207 if (DEBUG_SVUID)
1208 msg( "\twriteUTF( \"" + mname + "\" )" ) ;
1209 data.writeUTF(mname);
1211 // mask out post-1.4 modifiers
1212 int modifier = c.member.getModifiers() & METHOD_MASK ;
1214 if (DEBUG_SVUID)
1215 msg( "\twriteInt( " + modifier + " ) " ) ;
1216 data.writeInt( modifier ) ;
1218 if (DEBUG_SVUID)
1219 msg( "\twriteUTF( \"" + desc+ "\" )" ) ;
1220 data.writeUTF(desc);
1221 }
1223 /* Include in the hash all methods except those that are
1224 * private transient and private static.
1225 */
1226 MethodSignature[] methods =
1227 MethodSignature.removePrivateAndSort(method);
1228 for (int i = 0; i < methods.length; i++ ) {
1229 MethodSignature m = methods[i];
1230 String desc = m.signature;
1231 desc = desc.replace('/', '.');
1233 if (DEBUG_SVUID)
1234 msg( "\twriteUTF( \"" + m.member.getName()+ "\" )" ) ;
1235 data.writeUTF(m.member.getName());
1237 // mask out post-1.4 modifiers
1238 int modifier = m.member.getModifiers() & METHOD_MASK ;
1240 if (DEBUG_SVUID)
1241 msg( "\twriteInt( " + modifier + " ) " ) ;
1242 data.writeInt( modifier ) ;
1244 if (DEBUG_SVUID)
1245 msg( "\twriteUTF( \"" + desc + "\" )" ) ;
1246 data.writeUTF(desc);
1247 }
1249 /* Compute the hash value for this class.
1250 * Use only the first 64 bits of the hash.
1251 */
1252 data.flush();
1253 byte hasharray[] = md.digest();
1254 for (int i = 0; i < Math.min(8, hasharray.length); i++) {
1255 h += (long)(hasharray[i] & 255) << (i * 8);
1256 }
1257 } catch (IOException ignore) {
1258 /* can't happen, but be deterministic anyway. */
1259 h = -1;
1260 } catch (NoSuchAlgorithmException complain) {
1261 SecurityException se = new SecurityException() ;
1262 se.initCause( complain ) ;
1263 throw se ;
1264 }
1266 return h;
1267 }
1269 private static long computeStructuralUID(com.sun.corba.se.impl.io.ObjectStreamClass osc, Class cl) {
1270 ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
1272 long h = 0;
1273 try {
1275 if ((!java.io.Serializable.class.isAssignableFrom(cl)) ||
1276 (cl.isInterface())){
1277 return 0;
1278 }
1280 if (java.io.Externalizable.class.isAssignableFrom(cl)) {
1281 return 1;
1282 }
1284 MessageDigest md = MessageDigest.getInstance("SHA");
1285 DigestOutputStream mdo = new DigestOutputStream(devnull, md);
1286 DataOutputStream data = new DataOutputStream(mdo);
1288 // Get SUID of parent
1289 Class parent = cl.getSuperclass();
1290 if ((parent != null))
1291 // SerialBug 1; acc. to spec the one for
1292 // java.lang.object
1293 // should be computed and put
1294 // && (parent != java.lang.Object.class))
1295 {
1296 //data.writeLong(computeSerialVersionUID(null,parent));
1297 data.writeLong(computeStructuralUID(lookup(parent), parent));
1298 }
1300 if (osc.hasWriteObject())
1301 data.writeInt(2);
1302 else
1303 data.writeInt(1);
1305 // CORBA formal 00-11-03 10.6.2: For each field of the
1306 // class that is mapped to IDL, sorted lexicographically
1307 // by Java field name, in increasing order...
1308 ObjectStreamField[] field = osc.getFields();
1309 if (field.length > 1) {
1310 Arrays.sort(field, compareObjStrFieldsByName);
1311 }
1313 // ...Java field name in UTF encoding, field
1314 // descriptor, as defined by the JVM spec...
1315 for (int i = 0; i < field.length; i++) {
1316 data.writeUTF(field[i].getName());
1317 data.writeUTF(field[i].getSignature());
1318 }
1320 /* Compute the hash value for this class.
1321 * Use only the first 64 bits of the hash.
1322 */
1323 data.flush();
1324 byte hasharray[] = md.digest();
1325 // int minimum = Math.min(8, hasharray.length);
1326 // SerialBug 3: SHA computation is wrong; for loop reversed
1327 //for (int i = minimum; i > 0; i--)
1328 for (int i = 0; i < Math.min(8, hasharray.length); i++) {
1329 h += (long)(hasharray[i] & 255) << (i * 8);
1330 }
1331 } catch (IOException ignore) {
1332 /* can't happen, but be deterministic anyway. */
1333 h = -1;
1334 } catch (NoSuchAlgorithmException complain) {
1335 SecurityException se = new SecurityException();
1336 se.initCause( complain ) ;
1337 throw se ;
1338 }
1339 return h;
1340 }
1342 /**
1343 * Compute the JVM signature for the class.
1344 */
1345 static String getSignature(Class clazz) {
1346 String type = null;
1347 if (clazz.isArray()) {
1348 Class cl = clazz;
1349 int dimensions = 0;
1350 while (cl.isArray()) {
1351 dimensions++;
1352 cl = cl.getComponentType();
1353 }
1354 StringBuffer sb = new StringBuffer();
1355 for (int i = 0; i < dimensions; i++) {
1356 sb.append("[");
1357 }
1358 sb.append(getSignature(cl));
1359 type = sb.toString();
1360 } else if (clazz.isPrimitive()) {
1361 if (clazz == Integer.TYPE) {
1362 type = "I";
1363 } else if (clazz == Byte.TYPE) {
1364 type = "B";
1365 } else if (clazz == Long.TYPE) {
1366 type = "J";
1367 } else if (clazz == Float.TYPE) {
1368 type = "F";
1369 } else if (clazz == Double.TYPE) {
1370 type = "D";
1371 } else if (clazz == Short.TYPE) {
1372 type = "S";
1373 } else if (clazz == Character.TYPE) {
1374 type = "C";
1375 } else if (clazz == Boolean.TYPE) {
1376 type = "Z";
1377 } else if (clazz == Void.TYPE) {
1378 type = "V";
1379 }
1380 } else {
1381 type = "L" + clazz.getName().replace('.', '/') + ";";
1382 }
1383 return type;
1384 }
1386 /*
1387 * Compute the JVM method descriptor for the method.
1388 */
1389 static String getSignature(Method meth) {
1390 StringBuffer sb = new StringBuffer();
1392 sb.append("(");
1394 Class[] params = meth.getParameterTypes(); // avoid clone
1395 for (int j = 0; j < params.length; j++) {
1396 sb.append(getSignature(params[j]));
1397 }
1398 sb.append(")");
1399 sb.append(getSignature(meth.getReturnType()));
1400 return sb.toString();
1401 }
1403 /*
1404 * Compute the JVM constructor descriptor for the constructor.
1405 */
1406 static String getSignature(Constructor cons) {
1407 StringBuffer sb = new StringBuffer();
1409 sb.append("(");
1411 Class[] params = cons.getParameterTypes(); // avoid clone
1412 for (int j = 0; j < params.length; j++) {
1413 sb.append(getSignature(params[j]));
1414 }
1415 sb.append(")V");
1416 return sb.toString();
1417 }
1419 /*
1420 * Cache of Class -> ClassDescriptor Mappings.
1421 */
1422 static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61];
1424 /*
1425 * findDescriptorFor a Class. This looks in the cache for a
1426 * mapping from Class -> ObjectStreamClass mappings. The hashCode
1427 * of the Class is used for the lookup since the Class is the key.
1428 * The entries are extended from java.lang.ref.SoftReference so the
1429 * gc will be able to free them if needed.
1430 */
1431 private static ObjectStreamClass findDescriptorFor(Class cl) {
1433 int hash = cl.hashCode();
1434 int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
1435 ObjectStreamClassEntry e;
1436 ObjectStreamClassEntry prev;
1438 /* Free any initial entries whose refs have been cleared */
1439 while ((e = descriptorFor[index]) != null && e.get() == null) {
1440 descriptorFor[index] = e.next;
1441 }
1443 /* Traverse the chain looking for a descriptor with ofClass == cl.
1444 * unlink entries that are unresolved.
1445 */
1446 prev = e;
1447 while (e != null ) {
1448 ObjectStreamClass desc = (ObjectStreamClass)(e.get());
1449 if (desc == null) {
1450 // This entry has been cleared, unlink it
1451 prev.next = e.next;
1452 } else {
1453 if (desc.ofClass == cl)
1454 return desc;
1455 prev = e;
1456 }
1457 e = e.next;
1458 }
1459 return null;
1460 }
1462 /*
1463 * insertDescriptorFor a Class -> ObjectStreamClass mapping.
1464 */
1465 private static void insertDescriptorFor(ObjectStreamClass desc) {
1466 // Make sure not already present
1467 if (findDescriptorFor(desc.ofClass) != null) {
1468 return;
1469 }
1471 int hash = desc.ofClass.hashCode();
1472 int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
1473 ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc);
1474 e.next = descriptorFor[index];
1475 descriptorFor[index] = e;
1476 }
1478 private static Field[] getDeclaredFields(final Class clz) {
1479 return (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
1480 public Object run() {
1481 return clz.getDeclaredFields();
1482 }
1483 });
1484 }
1487 /*
1488 * The name of this descriptor
1489 */
1490 private String name;
1492 /*
1493 * The descriptor of the supertype.
1494 */
1495 private ObjectStreamClass superclass;
1497 /*
1498 * Flags for Serializable and Externalizable.
1499 */
1500 private boolean serializable;
1501 private boolean externalizable;
1503 /*
1504 * Array of persistent fields of this class, sorted by
1505 * type and name.
1506 */
1507 private ObjectStreamField[] fields;
1509 /*
1510 * Class that is a descriptor for in this virtual machine.
1511 */
1512 private Class ofClass;
1514 /*
1515 * True if descriptor for a proxy class.
1516 */
1517 boolean forProxyClass;
1520 /*
1521 * SerialVersionUID for this class.
1522 */
1523 private long suid = kDefaultUID;
1524 private String suidStr = null;
1526 /*
1527 * Actual (computed) SerialVersionUID for this class.
1528 */
1529 private long actualSuid = kDefaultUID;
1530 private String actualSuidStr = null;
1532 /*
1533 * The total number of bytes of primitive fields.
1534 * The total number of object fields.
1535 */
1536 int primBytes;
1537 int objFields;
1539 /**
1540 * Flag indicating whether or not this instance has
1541 * successfully completed initialization. This is to
1542 * try to fix bug 4373844. Working to move to
1543 * reusing java.io.ObjectStreamClass for JDK 1.5.
1544 */
1545 private boolean initialized = false;
1547 /* Internal lock object. */
1548 private Object lock = new Object();
1550 /* In JDK 1.1, external data was not written in block mode.
1551 * As of JDK 1.2, external data is written in block data mode. This
1552 * flag enables JDK 1.2 to be able to read JDK 1.1 written external data.
1553 *
1554 * @since JDK 1.2
1555 */
1556 private boolean hasExternalizableBlockData;
1557 Method writeObjectMethod;
1558 Method readObjectMethod;
1559 private Method writeReplaceObjectMethod;
1560 private Method readResolveObjectMethod;
1561 private Constructor cons ;
1563 /**
1564 * Beginning in Java to IDL ptc/02-01-12, RMI-IIOP has a
1565 * stream format version 2 which puts a fake valuetype around
1566 * a Serializable's optional custom data. This valuetype has
1567 * a special repository ID made from the Serializable's
1568 * information which we are pre-computing and
1569 * storing here.
1570 */
1571 private String rmiiiopOptionalDataRepId = null;
1573 /*
1574 * ObjectStreamClass that this one was built from.
1575 */
1576 private ObjectStreamClass localClassDesc;
1578 /* Find out if the class has a static class initializer <clinit> */
1579 private static Method hasStaticInitializerMethod = null;
1580 /**
1581 * Returns true if the given class defines a static initializer method,
1582 * false otherwise.
1583 */
1584 private static boolean hasStaticInitializer(Class cl) {
1585 if (hasStaticInitializerMethod == null) {
1586 Class classWithThisMethod = null;
1588 try {
1589 try {
1590 // When using rip-int with Merlin or when this is a Merlin
1591 // workspace, the method we want is in sun.misc.ClassReflector
1592 // and absent from java.io.ObjectStreamClass.
1593 //
1594 // When compiling rip-int with JDK 1.3.x, we have to get it
1595 // from java.io.ObjectStreamClass.
1596 classWithThisMethod = Class.forName("sun.misc.ClassReflector");
1597 } catch (ClassNotFoundException cnfe) {
1598 // Do nothing. This is either not a Merlin workspace,
1599 // or rip-int is being compiled with something other than
1600 // Merlin, probably JDK 1.3. Fall back on java.io.ObjectStreaClass.
1601 }
1602 if (classWithThisMethod == null)
1603 classWithThisMethod = java.io.ObjectStreamClass.class;
1605 hasStaticInitializerMethod =
1606 classWithThisMethod.getDeclaredMethod("hasStaticInitializer",
1607 new Class[] { Class.class });
1608 } catch (NoSuchMethodException ex) {
1609 }
1611 if (hasStaticInitializerMethod == null) {
1612 // XXX I18N, logging needed
1613 throw new InternalError("Can't find hasStaticInitializer method on "
1614 + classWithThisMethod.getName());
1615 }
1616 hasStaticInitializerMethod.setAccessible(true);
1617 }
1619 try {
1620 Boolean retval = (Boolean)
1621 hasStaticInitializerMethod.invoke(null, new Object[] { cl });
1622 return retval.booleanValue();
1623 } catch (Exception ex) {
1624 // XXX I18N, logging needed
1625 InternalError ie = new InternalError( "Error invoking hasStaticInitializer" ) ;
1626 ie.initCause( ex ) ;
1627 throw ie ;
1628 }
1629 }
1632 /* The Class Object for java.io.Serializable */
1633 private static Class classSerializable = null;
1634 private static Class classExternalizable = null;
1636 /*
1637 * Resolve java.io.Serializable at load time.
1638 */
1639 static {
1640 try {
1641 classSerializable = Class.forName("java.io.Serializable");
1642 classExternalizable = Class.forName("java.io.Externalizable");
1643 } catch (Throwable e) {
1644 System.err.println("Could not load java.io.Serializable or java.io.Externalizable.");
1645 }
1646 }
1648 /** use serialVersionUID from JDK 1.1. for interoperability */
1649 private static final long serialVersionUID = -6120832682080437368L;
1651 /**
1652 * Set serialPersistentFields of a Serializable class to this value to
1653 * denote that the class has no Serializable fields.
1654 */
1655 public static final ObjectStreamField[] NO_FIELDS =
1656 new ObjectStreamField[0];
1658 /*
1659 * Entries held in the Cache of known ObjectStreamClass objects.
1660 * Entries are chained together with the same hash value (modulo array size).
1661 */
1662 private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference
1663 {
1664 ObjectStreamClassEntry(ObjectStreamClass c) {
1665 //super(c);
1666 this.c = c;
1667 }
1668 ObjectStreamClassEntry next;
1670 public Object get()
1671 {
1672 return c;
1673 }
1674 private ObjectStreamClass c;
1675 }
1677 /*
1678 * Comparator object for Classes and Interfaces
1679 */
1680 private static Comparator compareClassByName =
1681 new CompareClassByName();
1683 private static class CompareClassByName implements Comparator {
1684 public int compare(Object o1, Object o2) {
1685 Class c1 = (Class)o1;
1686 Class c2 = (Class)o2;
1687 return (c1.getName()).compareTo(c2.getName());
1688 }
1689 }
1691 /**
1692 * Comparator for ObjectStreamFields by name
1693 */
1694 private final static Comparator compareObjStrFieldsByName
1695 = new CompareObjStrFieldsByName();
1697 private static class CompareObjStrFieldsByName implements Comparator {
1698 public int compare(Object o1, Object o2) {
1699 ObjectStreamField osf1 = (ObjectStreamField)o1;
1700 ObjectStreamField osf2 = (ObjectStreamField)o2;
1702 return osf1.getName().compareTo(osf2.getName());
1703 }
1704 }
1706 /*
1707 * Comparator object for Members, Fields, and Methods
1708 */
1709 private static Comparator compareMemberByName =
1710 new CompareMemberByName();
1712 private static class CompareMemberByName implements Comparator {
1713 public int compare(Object o1, Object o2) {
1714 String s1 = ((Member)o1).getName();
1715 String s2 = ((Member)o2).getName();
1717 if (o1 instanceof Method) {
1718 s1 += getSignature((Method)o1);
1719 s2 += getSignature((Method)o2);
1720 } else if (o1 instanceof Constructor) {
1721 s1 += getSignature((Constructor)o1);
1722 s2 += getSignature((Constructor)o2);
1723 }
1724 return s1.compareTo(s2);
1725 }
1726 }
1728 /* It is expensive to recompute a method or constructor signature
1729 many times, so compute it only once using this data structure. */
1730 private static class MethodSignature implements Comparator {
1731 Member member;
1732 String signature; // cached parameter signature
1734 /* Given an array of Method or Constructor members,
1735 return a sorted array of the non-private members.*/
1736 /* A better implementation would be to implement the returned data
1737 structure as an insertion sorted link list.*/
1738 static MethodSignature[] removePrivateAndSort(Member[] m) {
1739 int numNonPrivate = 0;
1740 for (int i = 0; i < m.length; i++) {
1741 if (! Modifier.isPrivate(m[i].getModifiers())) {
1742 numNonPrivate++;
1743 }
1744 }
1745 MethodSignature[] cm = new MethodSignature[numNonPrivate];
1746 int cmi = 0;
1747 for (int i = 0; i < m.length; i++) {
1748 if (! Modifier.isPrivate(m[i].getModifiers())) {
1749 cm[cmi] = new MethodSignature(m[i]);
1750 cmi++;
1751 }
1752 }
1753 if (cmi > 0)
1754 Arrays.sort(cm, cm[0]);
1755 return cm;
1756 }
1758 /* Assumes that o1 and o2 are either both methods
1759 or both constructors.*/
1760 public int compare(Object o1, Object o2) {
1761 /* Arrays.sort calls compare when o1 and o2 are equal.*/
1762 if (o1 == o2)
1763 return 0;
1765 MethodSignature c1 = (MethodSignature)o1;
1766 MethodSignature c2 = (MethodSignature)o2;
1768 int result;
1769 if (isConstructor()) {
1770 result = c1.signature.compareTo(c2.signature);
1771 } else { // is a Method.
1772 result = c1.member.getName().compareTo(c2.member.getName());
1773 if (result == 0)
1774 result = c1.signature.compareTo(c2.signature);
1775 }
1776 return result;
1777 }
1779 final private boolean isConstructor() {
1780 return member instanceof Constructor;
1781 }
1782 private MethodSignature(Member m) {
1783 member = m;
1784 if (isConstructor()) {
1785 signature = ObjectStreamClass.getSignature((Constructor)m);
1786 } else {
1787 signature = ObjectStreamClass.getSignature((Method)m);
1788 }
1789 }
1790 }
1792 /**
1793 * Returns non-static, non-abstract method with given signature provided it
1794 * is defined by or accessible (via inheritance) by the given class, or
1795 * null if no match found. Access checks are disabled on the returned
1796 * method (if any).
1797 *
1798 * Copied from the Merlin java.io.ObjectStreamClass.
1799 */
1800 private static Method getInheritableMethod(Class cl, String name,
1801 Class[] argTypes,
1802 Class returnType)
1803 {
1804 Method meth = null;
1805 Class defCl = cl;
1806 while (defCl != null) {
1807 try {
1808 meth = defCl.getDeclaredMethod(name, argTypes);
1809 break;
1810 } catch (NoSuchMethodException ex) {
1811 defCl = defCl.getSuperclass();
1812 }
1813 }
1815 if ((meth == null) || (meth.getReturnType() != returnType)) {
1816 return null;
1817 }
1818 meth.setAccessible(true);
1819 int mods = meth.getModifiers();
1820 if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) {
1821 return null;
1822 } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
1823 return meth;
1824 } else if ((mods & Modifier.PRIVATE) != 0) {
1825 return (cl == defCl) ? meth : null;
1826 } else {
1827 return packageEquals(cl, defCl) ? meth : null;
1828 }
1829 }
1831 /**
1832 * Returns true if classes are defined in the same package, false
1833 * otherwise.
1834 *
1835 * Copied from the Merlin java.io.ObjectStreamClass.
1836 */
1837 private static boolean packageEquals(Class cl1, Class cl2) {
1838 Package pkg1 = cl1.getPackage(), pkg2 = cl2.getPackage();
1839 return ((pkg1 == pkg2) || ((pkg1 != null) && (pkg1.equals(pkg2))));
1840 }
1841 }