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