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