1 /* ObjectStreamClass.java -- Class used to write class information
2 about serialized objects.
3 Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
42 import gnu
.java
.io
.NullOutputStream
;
43 import gnu
.java
.lang
.reflect
.TypeSignature
;
44 import gnu
.java
.security
.action
.SetAccessibleAction
;
45 import gnu
.java
.security
.provider
.Gnu
;
47 import java
.lang
.reflect
.Constructor
;
48 import java
.lang
.reflect
.Field
;
49 import java
.lang
.reflect
.Member
;
50 import java
.lang
.reflect
.Method
;
51 import java
.lang
.reflect
.Modifier
;
52 import java
.lang
.reflect
.Proxy
;
53 import java
.security
.AccessController
;
54 import java
.security
.DigestOutputStream
;
55 import java
.security
.MessageDigest
;
56 import java
.security
.NoSuchAlgorithmException
;
57 import java
.security
.PrivilegedAction
;
58 import java
.security
.Security
;
59 import java
.util
.Arrays
;
60 import java
.util
.Comparator
;
61 import java
.util
.Hashtable
;
62 import java
.util
.Vector
;
64 public class ObjectStreamClass
implements Serializable
67 * Returns the <code>ObjectStreamClass</code> for <code>cl</code>.
68 * If <code>cl</code> is null, or is not <code>Serializable</code>,
69 * null is returned. <code>ObjectStreamClass</code>'s are memorized;
70 * later calls to this method with the same class will return the
71 * same <code>ObjectStreamClass</code> object and no recalculation
74 * @see java.io.Serializable
76 public static ObjectStreamClass
lookup(Class cl
)
80 if (! (Serializable
.class).isAssignableFrom(cl
))
83 return lookupForClassObject(cl
);
87 * This lookup for internal use by ObjectOutputStream. Suppose
88 * we have a java.lang.Class object C for class A, though A is not
89 * serializable, but it's okay to serialize C.
91 static ObjectStreamClass
lookupForClassObject(Class cl
)
96 ObjectStreamClass osc
= (ObjectStreamClass
) classLookupTable
.get(cl
);
102 osc
= new ObjectStreamClass(cl
);
103 classLookupTable
.put(cl
, osc
);
109 * Returns the name of the class that this
110 * <code>ObjectStreamClass</code> represents.
112 * @return the name of the class.
114 public String
getName()
120 * Returns the class that this <code>ObjectStreamClass</code>
121 * represents. Null could be returned if this
122 * <code>ObjectStreamClass</code> was read from an
123 * <code>ObjectInputStream</code> and the class it represents cannot
124 * be found or loaded.
126 * @see java.io.ObjectInputStream
128 public Class
forClass()
134 * Returns the serial version stream-unique identifier for the class
135 * represented by this <code>ObjectStreamClass</code>. This SUID is
136 * either defined by the class as <code>static final long
137 * serialVersionUID</code> or is calculated as specified in
138 * Javasoft's "Object Serialization Specification" XXX: add reference
140 * @return the serial version UID.
142 public long getSerialVersionUID()
148 * Returns the serializable (non-static and non-transient) Fields
149 * of the class represented by this ObjectStreamClass. The Fields
150 * are sorted by name.
152 * @return the fields.
154 public ObjectStreamField
[] getFields()
156 ObjectStreamField
[] copy
= new ObjectStreamField
[ fields
.length
];
157 System
.arraycopy(fields
, 0, copy
, 0, fields
.length
);
162 // Can't do binary search since fields is sorted by name and
164 public ObjectStreamField
getField (String name
)
166 for (int i
= 0; i
< fields
.length
; i
++)
167 if (fields
[i
].getName().equals(name
))
173 * Returns a textual representation of this
174 * <code>ObjectStreamClass</code> object including the name of the
175 * class it represents as well as that class's serial version
176 * stream-unique identifier.
178 * @see #getSerialVersionUID()
181 public String
toString()
183 return "java.io.ObjectStreamClass< " + name
+ ", " + uid
+ " >";
186 // Returns true iff the class that this ObjectStreamClass represents
187 // has the following method:
189 // private void writeObject (ObjectOutputStream)
191 // This method is used by the class to override default
192 // serialization behavior.
193 boolean hasWriteMethod()
195 return (flags
& ObjectStreamConstants
.SC_WRITE_METHOD
) != 0;
198 // Returns true iff the class that this ObjectStreamClass represents
199 // implements Serializable but does *not* implement Externalizable.
200 boolean isSerializable()
202 return (flags
& ObjectStreamConstants
.SC_SERIALIZABLE
) != 0;
206 // Returns true iff the class that this ObjectStreamClass represents
207 // implements Externalizable.
208 boolean isExternalizable()
210 return (flags
& ObjectStreamConstants
.SC_EXTERNALIZABLE
) != 0;
214 // Returns the <code>ObjectStreamClass</code> that represents the
215 // class that is the superclass of the class this
216 // <code>ObjectStreamClass</code> represents. If the superclass is
217 // not Serializable, null is returned.
218 ObjectStreamClass
getSuper()
224 // returns an array of ObjectStreamClasses that represent the super
225 // classes of CLAZZ and CLAZZ itself in order from most super to
226 // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ
227 // that is serializable.
228 static ObjectStreamClass
[] getObjectStreamClasses(Class clazz
)
230 ObjectStreamClass osc
= ObjectStreamClass
.lookup(clazz
);
233 return new ObjectStreamClass
[0];
236 Vector oscs
= new Vector();
240 oscs
.addElement (osc
);
241 osc
= osc
.getSuper();
244 int count
= oscs
.size();
245 ObjectStreamClass
[] sorted_oscs
= new ObjectStreamClass
[ count
];
247 for (int i
= count
- 1; i
>= 0; i
--)
248 sorted_oscs
[ count
- i
- 1 ] = (ObjectStreamClass
) oscs
.elementAt(i
);
255 // Returns an integer that consists of bit-flags that indicate
256 // properties of the class represented by this ObjectStreamClass.
257 // The bit-flags that could be present are those defined in
258 // ObjectStreamConstants that begin with `SC_'
265 ObjectStreamClass(String name
, long uid
, byte flags
,
266 ObjectStreamField
[] fields
)
271 this.fields
= fields
;
275 * This method builds the internal description corresponding to a Java Class.
276 * As the constructor only assign a name to the current ObjectStreamClass instance,
277 * that method sets the serial UID, chose the fields which will be serialized,
278 * and compute the position of the fields in the serialized stream.
280 * @param cl The Java class which is used as a reference for building the descriptor.
281 * @param superClass The descriptor of the super class for this class descriptor.
282 * @throws InvalidClassException if an incompatibility between computed UID and
283 * already set UID is found.
285 void setClass(Class cl
, ObjectStreamClass superClass
) throws InvalidClassException
291 long class_uid
= getClassUID(cl
);
296 // Check that the actual UID of the resolved class matches the UID from
298 if (uid
!= class_uid
)
301 ": Local class not compatible: stream serialVersionUID="
302 + uid
+ ", local serialVersionUID=" + class_uid
;
303 throw new InvalidClassException (msg
);
307 isProxyClass
= clazz
!= null && Proxy
.isProxyClass(clazz
);
308 this.superClass
= superClass
;
313 ObjectStreamField
[] exportedFields
= getSerialPersistentFields (clazz
);
315 if (exportedFields
== null)
318 ObjectStreamField
[] newFieldList
= new ObjectStreamField
[exportedFields
.length
+ fields
.length
];
321 /* We now check the import fields against the exported fields.
322 * There should not be contradiction (e.g. int x and String x)
323 * but extra virtual fields can be added to the class.
326 Arrays
.sort(exportedFields
);
329 while (i
< fields
.length
&& j
< exportedFields
.length
)
331 int comp
= fields
[i
].compareTo(exportedFields
[j
]);
335 newFieldList
[k
] = fields
[i
];
336 fields
[i
].setPersistent(false);
337 fields
[i
].setToSet(false);
342 /* field not found in imported fields. We add it
343 * in the list of supported fields.
345 newFieldList
[k
] = exportedFields
[j
];
346 newFieldList
[k
].setPersistent(true);
347 newFieldList
[k
].setToSet(false);
350 newFieldList
[k
].lookupField(clazz
);
351 newFieldList
[k
].checkFieldType();
353 catch (NoSuchFieldException _
)
362 exportedFields
[j
].lookupField(clazz
);
363 exportedFields
[j
].checkFieldType();
365 catch (NoSuchFieldException _
)
369 if (!fields
[i
].getType().equals(exportedFields
[j
].getType()))
370 throw new InvalidClassException
371 ("serialPersistentFields must be compatible with" +
372 " imported fields (about " + fields
[i
].getName() + ")");
373 newFieldList
[k
] = fields
[i
];
374 fields
[i
].setPersistent(true);
381 if (i
< fields
.length
)
382 for (;i
<fields
.length
;i
++,k
++)
384 fields
[i
].setPersistent(false);
385 fields
[i
].setToSet(false);
386 newFieldList
[k
] = fields
[i
];
389 if (j
< exportedFields
.length
)
390 for (;j
<exportedFields
.length
;j
++,k
++)
392 exportedFields
[j
].setPersistent(true);
393 exportedFields
[j
].setToSet(false);
394 newFieldList
[k
] = exportedFields
[j
];
397 fields
= new ObjectStreamField
[k
];
398 System
.arraycopy(newFieldList
, 0, fields
, 0, k
);
400 catch (NoSuchFieldException ignore
)
404 catch (IllegalAccessException ignore
)
410 void setSuperclass (ObjectStreamClass osc
)
415 void calculateOffsets()
418 ObjectStreamField field
;
420 int fcount
= fields
.length
;
421 for (i
= 0; i
< fcount
; ++ i
)
425 if (! field
.isPrimitive())
428 field
.setOffset(primFieldSize
);
429 switch (field
.getTypeCode())
450 for (objectFieldCount
= 0; i
< fcount
; ++ i
)
451 fields
[i
].setOffset(objectFieldCount
++);
454 private Method
findMethod(Method
[] methods
, String name
, Class
[] params
,
455 Class returnType
, boolean mustBePrivate
)
458 for (int i
= 0; i
< methods
.length
; i
++)
460 final Method m
= methods
[i
];
461 int mods
= m
.getModifiers();
462 if (Modifier
.isStatic(mods
)
463 || (mustBePrivate
&& !Modifier
.isPrivate(mods
)))
468 if (m
.getName().equals(name
)
469 && m
.getReturnType() == returnType
)
471 Class
[] mp
= m
.getParameterTypes();
472 if (mp
.length
== params
.length
)
474 for (int j
= 0; j
< mp
.length
; j
++)
476 if (mp
[j
] != params
[j
])
481 AccessController
.doPrivileged(new SetAccessibleAction(m
));
489 private void cacheMethods()
491 Method
[] methods
= forClass().getDeclaredMethods();
492 readObjectMethod
= findMethod(methods
, "readObject",
493 new Class
[] { ObjectInputStream
.class },
495 writeObjectMethod
= findMethod(methods
, "writeObject",
496 new Class
[] { ObjectOutputStream
.class },
498 readResolveMethod
= findMethod(methods
, "readResolve",
499 new Class
[0], Object
.class, false);
500 writeReplaceMethod
= findMethod(methods
, "writeReplace",
501 new Class
[0], Object
.class, false);
504 private ObjectStreamClass(Class cl
)
508 isProxyClass
= Proxy
.isProxyClass(cl
);
515 // to those class nonserializable, its uid field is 0
516 if ( (Serializable
.class).isAssignableFrom(cl
) && !isProxyClass
)
517 uid
= getClassUID(cl
);
518 superClass
= lookup(cl
.getSuperclass());
522 // Sets bits in flags according to features of CL.
523 private void setFlags(Class cl
)
525 if ((java
.io
.Externalizable
.class).isAssignableFrom(cl
))
526 flags
|= ObjectStreamConstants
.SC_EXTERNALIZABLE
;
527 else if ((java
.io
.Serializable
.class).isAssignableFrom(cl
))
528 // only set this bit if CL is NOT Externalizable
529 flags
|= ObjectStreamConstants
.SC_SERIALIZABLE
;
531 if (writeObjectMethod
!= null)
532 flags
|= ObjectStreamConstants
.SC_WRITE_METHOD
;
536 // Sets fields to be a sorted array of the serializable fields of
538 private void setFields(Class cl
)
540 SetAccessibleAction setAccessible
= new SetAccessibleAction();
542 if (!isSerializable() || isExternalizable())
551 cl
.getDeclaredField("serialPersistentFields");
552 setAccessible
.setMember(f
);
553 AccessController
.doPrivileged(setAccessible
);
554 int modifiers
= f
.getModifiers();
556 if (Modifier
.isStatic(modifiers
)
557 && Modifier
.isFinal(modifiers
)
558 && Modifier
.isPrivate(modifiers
))
560 fields
= getSerialPersistentFields(cl
);
563 Arrays
.sort (fields
);
564 // Retrieve field reference.
565 for (int i
=0; i
< fields
.length
; i
++)
569 fields
[i
].lookupField(cl
);
571 catch (NoSuchFieldException _
)
573 fields
[i
].setToSet(false);
582 catch (NoSuchFieldException ignore
)
585 catch (IllegalAccessException ignore
)
589 int num_good_fields
= 0;
590 Field
[] all_fields
= cl
.getDeclaredFields();
593 // set non-serializable fields to null in all_fields
594 for (int i
= 0; i
< all_fields
.length
; i
++)
596 modifiers
= all_fields
[i
].getModifiers();
597 if (Modifier
.isTransient(modifiers
)
598 || Modifier
.isStatic(modifiers
))
599 all_fields
[i
] = null;
604 // make a copy of serializable (non-null) fields
605 fields
= new ObjectStreamField
[ num_good_fields
];
606 for (int from
= 0, to
= 0; from
< all_fields
.length
; from
++)
607 if (all_fields
[from
] != null)
609 final Field f
= all_fields
[from
];
610 setAccessible
.setMember(f
);
611 AccessController
.doPrivileged(setAccessible
);
612 fields
[to
] = new ObjectStreamField(all_fields
[from
]);
617 // Make sure we don't have any duplicate field names
618 // (Sun JDK 1.4.1. throws an Internal Error as well)
619 for (int i
= 1; i
< fields
.length
; i
++)
621 if(fields
[i
- 1].getName().equals(fields
[i
].getName()))
622 throw new InternalError("Duplicate field " +
623 fields
[i
].getName() + " in class " + cl
.getName());
628 // Returns the serial version UID defined by class, or if that
629 // isn't present, calculates value of serial version UID.
630 private long getClassUID(Class cl
)
634 // Use getDeclaredField rather than getField, since serialVersionUID
635 // may not be public AND we only want the serialVersionUID of this
636 // class, not a superclass or interface.
637 final Field suid
= cl
.getDeclaredField("serialVersionUID");
638 SetAccessibleAction setAccessible
= new SetAccessibleAction(suid
);
639 AccessController
.doPrivileged(setAccessible
);
640 int modifiers
= suid
.getModifiers();
642 if (Modifier
.isStatic(modifiers
)
643 && Modifier
.isFinal(modifiers
)
644 && suid
.getType() == Long
.TYPE
)
645 return suid
.getLong(null);
647 catch (NoSuchFieldException ignore
)
650 catch (IllegalAccessException ignore
)
654 // cl didn't define serialVersionUID, so we have to compute it
660 md
= MessageDigest
.getInstance("SHA");
662 catch (NoSuchAlgorithmException e
)
664 // If a provider already provides SHA, use it; otherwise, use this.
665 Gnu gnuProvider
= new Gnu();
666 Security
.addProvider(gnuProvider
);
667 md
= MessageDigest
.getInstance("SHA");
670 DigestOutputStream digest_out
=
671 new DigestOutputStream(nullOutputStream
, md
);
672 DataOutputStream data_out
= new DataOutputStream(digest_out
);
674 data_out
.writeUTF(cl
.getName());
676 int modifiers
= cl
.getModifiers();
677 // just look at interesting bits
678 modifiers
= modifiers
& (Modifier
.ABSTRACT
| Modifier
.FINAL
679 | Modifier
.INTERFACE
| Modifier
.PUBLIC
);
680 data_out
.writeInt(modifiers
);
682 // Pretend that an array has no interfaces, because when array
683 // serialization was defined (JDK 1.1), arrays didn't have it.
686 Class
[] interfaces
= cl
.getInterfaces();
687 Arrays
.sort(interfaces
, interfaceComparator
);
688 for (int i
= 0; i
< interfaces
.length
; i
++)
689 data_out
.writeUTF(interfaces
[i
].getName());
693 Field
[] fields
= cl
.getDeclaredFields();
694 Arrays
.sort(fields
, memberComparator
);
695 for (int i
= 0; i
< fields
.length
; i
++)
698 modifiers
= field
.getModifiers();
699 if (Modifier
.isPrivate(modifiers
)
700 && (Modifier
.isStatic(modifiers
)
701 || Modifier
.isTransient(modifiers
)))
704 data_out
.writeUTF(field
.getName());
705 data_out
.writeInt(modifiers
);
706 data_out
.writeUTF(TypeSignature
.getEncodingOfClass (field
.getType()));
709 // write class initializer method if present
710 if (VMObjectStreamClass
.hasClassInitializer(cl
))
712 data_out
.writeUTF("<clinit>");
713 data_out
.writeInt(Modifier
.STATIC
);
714 data_out
.writeUTF("()V");
717 Constructor constructor
;
718 Constructor
[] constructors
= cl
.getDeclaredConstructors();
719 Arrays
.sort (constructors
, memberComparator
);
720 for (int i
= 0; i
< constructors
.length
; i
++)
722 constructor
= constructors
[i
];
723 modifiers
= constructor
.getModifiers();
724 if (Modifier
.isPrivate(modifiers
))
727 data_out
.writeUTF("<init>");
728 data_out
.writeInt(modifiers
);
730 // the replacement of '/' with '.' was needed to make computed
731 // SUID's agree with those computed by JDK
733 (TypeSignature
.getEncodingOfConstructor(constructor
).replace('/','.'));
737 Method
[] methods
= cl
.getDeclaredMethods();
738 Arrays
.sort(methods
, memberComparator
);
739 for (int i
= 0; i
< methods
.length
; i
++)
742 modifiers
= method
.getModifiers();
743 if (Modifier
.isPrivate(modifiers
))
746 data_out
.writeUTF(method
.getName());
747 data_out
.writeInt(modifiers
);
749 // the replacement of '/' with '.' was needed to make computed
750 // SUID's agree with those computed by JDK
752 (TypeSignature
.getEncodingOfMethod(method
).replace('/', '.'));
756 byte[] sha
= md
.digest();
758 int len
= sha
.length
< 8 ? sha
.length
: 8;
759 for (int i
= 0; i
< len
; i
++)
760 result
+= (long) (sha
[i
] & 0xFF) << (8 * i
);
764 catch (NoSuchAlgorithmException e
)
766 throw new RuntimeException
767 ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
770 catch (IOException ioe
)
772 throw new RuntimeException(ioe
);
777 * Returns the value of CLAZZ's private static final field named
778 * `serialPersistentFields'. It performs some sanity checks before
779 * returning the real array. Besides, the returned array is a clean
780 * copy of the original. So it can be modified.
782 * @param clazz Class to retrieve 'serialPersistentFields' from.
783 * @return The content of 'serialPersistentFields'.
785 private ObjectStreamField
[] getSerialPersistentFields(Class clazz
)
786 throws NoSuchFieldException
, IllegalAccessException
788 ObjectStreamField
[] fieldsArray
= null;
789 ObjectStreamField
[] o
;
791 // Use getDeclaredField rather than getField for the same reason
792 // as above in getDefinedSUID.
793 Field f
= clazz
.getDeclaredField("serialPersistentFields");
794 f
.setAccessible(true);
796 int modifiers
= f
.getModifiers();
797 if (!(Modifier
.isStatic(modifiers
) &&
798 Modifier
.isFinal(modifiers
) &&
799 Modifier
.isPrivate(modifiers
)))
802 o
= (ObjectStreamField
[]) f
.get(null);
807 fieldsArray
= new ObjectStreamField
[ o
.length
];
808 System
.arraycopy(o
, 0, fieldsArray
, 0, o
.length
);
814 * Returns a new instance of the Class this ObjectStreamClass corresponds
816 * Note that this should only be used for Externalizable classes.
818 * @return A new instance.
820 Externalizable
newInstance() throws InvalidClassException
824 if (constructor
== null)
828 final Constructor c
= clazz
.getConstructor(new Class
[0]);
830 AccessController
.doPrivileged(new PrivilegedAction()
834 c
.setAccessible(true);
841 catch(NoSuchMethodException x
)
843 throw new InvalidClassException(clazz
.getName(),
844 "No public zero-argument constructor");
851 return (Externalizable
)constructor
.newInstance(null);
855 throw (InvalidClassException
)
856 new InvalidClassException(clazz
.getName(),
857 "Unable to instantiate").initCause(x
);
861 public static final ObjectStreamField
[] NO_FIELDS
= {};
863 private static Hashtable classLookupTable
= new Hashtable();
864 private static final NullOutputStream nullOutputStream
= new NullOutputStream();
865 private static final Comparator interfaceComparator
= new InterfaceComparator();
866 private static final Comparator memberComparator
= new MemberComparator();
868 Class
[] writeMethodArgTypes
= { java
.io
.ObjectOutputStream
.class };
870 private ObjectStreamClass superClass
;
876 // this field is package protected so that ObjectInputStream and
877 // ObjectOutputStream can access it directly
878 ObjectStreamField
[] fields
;
880 // these are accessed by ObjectIn/OutputStream
881 int primFieldSize
= -1; // -1 if not yet calculated
882 int objectFieldCount
;
884 Method readObjectMethod
;
885 Method readResolveMethod
;
886 Method writeReplaceMethod
;
887 Method writeObjectMethod
;
888 boolean realClassIsSerializable
;
889 boolean realClassIsExternalizable
;
890 ObjectStreamField
[] fieldMapping
;
891 Constructor firstNonSerializableParentConstructor
;
892 private Constructor constructor
; // default constructor for Externalizable
894 boolean isProxyClass
= false;
896 // This is probably not necessary because this class is special cased already
897 // but it will avoid showing up as a discrepancy when comparing SUIDs.
898 private static final long serialVersionUID
= -6120832682080437368L;
901 // interfaces are compared only by name
902 private static final class InterfaceComparator
implements Comparator
904 public int compare(Object o1
, Object o2
)
906 return ((Class
) o1
).getName().compareTo(((Class
) o2
).getName());
911 // Members (Methods and Constructors) are compared first by name,
912 // conflicts are resolved by comparing type signatures
913 private static final class MemberComparator
implements Comparator
915 public int compare(Object o1
, Object o2
)
917 Member m1
= (Member
) o1
;
918 Member m2
= (Member
) o2
;
920 int comp
= m1
.getName().compareTo(m2
.getName());
923 return TypeSignature
.getEncodingOfMember(m1
).
924 compareTo(TypeSignature
.getEncodingOfMember(m2
));