source: trunk/gcc/libjava/java/io/ObjectStreamClass.java@ 3394

Last change on this file since 3394 was 1392, checked in by bird, 22 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 19.1 KB
Line 
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.
4
5This file is part of GNU Classpath.
6
7GNU Classpath is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Classpath is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Classpath; see the file COPYING. If not, write to the
19Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
2002111-1307 USA.
21
22Linking this library statically or dynamically with other modules is
23making a combined work based on this library. Thus, the terms and
24conditions of the GNU General Public License cover the whole
25combination.
26
27As a special exception, the copyright holders of this library give you
28permission to link this library with independent modules to produce an
29executable, regardless of the license terms of these independent
30modules, and to copy and distribute the resulting executable under
31terms of your choice, provided that you also meet, for each linked
32independent module, the terms and conditions of the license of that
33module. An independent module is a module which is not derived from
34or based on this library. If you modify this library, you may extend
35this exception to your version of the library, but you are not
36obligated to do so. If you do not wish to do so, delete this
37exception statement from your version. */
38
39
40package java.io;
41
42import java.lang.reflect.Constructor;
43import java.lang.reflect.Field;
44import java.lang.reflect.Member;
45import java.lang.reflect.Method;
46import java.lang.reflect.Modifier;
47import java.lang.reflect.Proxy;
48import java.security.DigestOutputStream;
49import java.security.MessageDigest;
50import java.security.NoSuchAlgorithmException;
51import java.security.Security;
52import java.util.Arrays;
53import java.util.Comparator;
54import java.util.Hashtable;
55import java.util.Vector;
56import gnu.java.io.NullOutputStream;
57import gnu.java.lang.reflect.TypeSignature;
58import gnu.java.security.provider.Gnu;
59
60
61public class ObjectStreamClass implements Serializable
62{
63 /**
64 Returns the <code>ObjectStreamClass</code> for <code>cl</code>.
65 If <code>cl</code> is null, or is not <code>Serializable</code>,
66 null is returned. <code>ObjectStreamClass</code>'s are memorized;
67 later calls to this method with the same class will return the
68 same <code>ObjectStreamClass</code> object and no recalculation
69 will be done.
70
71 @see java.io.Serializable
72 */
73 public static ObjectStreamClass lookup (Class cl)
74 {
75 if (cl == null)
76 return null;
77 if (! (Serializable.class).isAssignableFrom (cl))
78 return null;
79
80 return lookupForClassObject (cl);
81 }
82
83 /**
84 * This lookup for internal use by ObjectOutputStream. Suppose
85 * we have a java.lang.Class object C for class A, though A is not
86 * serializable, but it's okay to serialize C.
87 */
88 static ObjectStreamClass lookupForClassObject (Class cl)
89 {
90 if (cl == null)
91 return null;
92
93 ObjectStreamClass osc = (ObjectStreamClass)classLookupTable.get (cl);
94
95 if (osc != null)
96 return osc;
97 else
98 {
99 osc = new ObjectStreamClass (cl);
100 classLookupTable.put (cl, osc);
101 return osc;
102 }
103 }
104
105
106 /**
107 Returns the name of the class that this
108 <code>ObjectStreamClass</code> represents.
109 */
110 public String getName ()
111 {
112 return name;
113 }
114
115
116 /**
117 Returns the class that this <code>ObjectStreamClass</code>
118 represents. Null could be returned if this
119 <code>ObjectStreamClass</code> was read from an
120 <code>ObjectInputStream</code> and the class it represents cannot
121 be found or loaded.
122
123 @see java.io.ObjectInputStream
124 */
125 public Class forClass ()
126 {
127 return clazz;
128 }
129
130
131 /**
132 Returns the serial version stream-unique identifier for the class
133 represented by this <code>ObjectStreamClass</code>. This SUID is
134 either defined by the class as <code>static final long
135 serialVersionUID</code> or is calculated as specified in
136 Javasoft's "Object Serialization Specification" XXX: add reference
137 */
138 public long getSerialVersionUID ()
139 {
140 return uid;
141 }
142
143
144 // Returns the serializable (non-static and non-transient) Fields
145 // of the class represented by this ObjectStreamClass. The Fields
146 // are sorted by name.
147 // XXX doc
148 public ObjectStreamField[] getFields ()
149 {
150 ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
151 System.arraycopy (fields, 0, copy, 0, fields.length);
152 return copy;
153 }
154
155
156 // XXX doc
157 // Can't do binary search since fields is sorted by name and
158 // primitiveness.
159 public ObjectStreamField getField (String name)
160 {
161 for (int i=0; i < fields.length; i++)
162 if (fields[i].getName ().equals (name))
163 return fields[i];
164 return null;
165 }
166
167
168 /**
169 Returns a textual representation of this
170 <code>ObjectStreamClass</code> object including the name of the
171 class it represents as well as that class's serial version
172 stream-unique identifier.
173
174 @see getSerialVersionUID ()
175 @see getName ()
176 */
177 public String toString ()
178 {
179 return "java.io.ObjectStreamClass< " + name + ", " + uid + " >";
180 }
181
182
183 // Returns true iff the class that this ObjectStreamClass represents
184 // has the following method:
185 //
186 // private void writeObject (ObjectOutputStream)
187 //
188 // This method is used by the class to override default
189 // serialization behavior.
190 boolean hasWriteMethod ()
191 {
192 return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
193 }
194
195
196 // Returns true iff the class that this ObjectStreamClass represents
197 // has the following method:
198 //
199 // private void readObject (ObjectOutputStream)
200 //
201 // This method is used by the class to override default
202 // serialization behavior.
203 boolean hasReadMethod ()
204 {
205 try
206 {
207 Class[] readObjectParams = { ObjectInputStream.class };
208 forClass ().getDeclaredMethod ("readObject", readObjectParams);
209 return true;
210 }
211 catch (NoSuchMethodException e)
212 {
213 return false;
214 }
215 }
216
217
218 // Returns true iff the class that this ObjectStreamClass represents
219 // implements Serializable but does *not* implement Externalizable.
220 boolean isSerializable ()
221 {
222 return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
223 }
224
225
226 // Returns true iff the class that this ObjectStreamClass represents
227 // implements Externalizable.
228 boolean isExternalizable ()
229 {
230 return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
231 }
232
233
234 // Returns the <code>ObjectStreamClass</code> that represents the
235 // class that is the superclass of the class this
236 // <code>ObjectStreamClass</code> represents. If the superclass is
237 // not Serializable, null is returned.
238 ObjectStreamClass getSuper ()
239 {
240 return superClass;
241 }
242
243
244 // returns an array of ObjectStreamClasses that represent the super
245 // classes of CLAZZ and CLAZZ itself in order from most super to
246 // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ
247 // that is serializable.
248 static ObjectStreamClass[] getObjectStreamClasses (Class clazz)
249 {
250 ObjectStreamClass osc = ObjectStreamClass.lookup (clazz);
251
252 ObjectStreamClass[] ret_val;
253
254 if (osc == null)
255 return new ObjectStreamClass[0];
256 else
257 {
258 Vector oscs = new Vector ();
259
260 while (osc != null)
261 {
262 oscs.addElement (osc);
263 osc = osc.getSuper ();
264 }
265
266 int count = oscs.size ();
267 ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ];
268
269 for (int i = count - 1; i >= 0; i--)
270 sorted_oscs[ count - i - 1 ] = (ObjectStreamClass)oscs.elementAt (i);
271
272 return sorted_oscs;
273 }
274 }
275
276
277 // Returns an integer that consists of bit-flags that indicate
278 // properties of the class represented by this ObjectStreamClass.
279 // The bit-flags that could be present are those defined in
280 // ObjectStreamConstants that begin with `SC_'
281 int getFlags ()
282 {
283 return flags;
284 }
285
286
287 ObjectStreamClass (String name, long uid, byte flags,
288 ObjectStreamField[] fields)
289 {
290 this.name = name;
291 this.uid = uid;
292 this.flags = flags;
293 this.fields = fields;
294 }
295
296 void setClass (Class cl) throws InvalidClassException
297 {
298 this.clazz = cl;
299
300 long class_uid = getClassUID (cl);
301 if (uid == 0)
302 uid = class_uid;
303 else
304 {
305 // Check that the actual UID of the resolved class matches the UID from
306 // the stream.
307 if (uid != class_uid)
308 {
309 String msg = cl +
310 ": Local class not compatible: stream serialVersionUID="
311 + uid + ", local serialVersionUID=" + class_uid;
312 throw new InvalidClassException (msg);
313 }
314 }
315
316 isProxyClass = clazz != null && Proxy.isProxyClass (clazz);
317 ObjectStreamClass osc = (ObjectStreamClass)classLookupTable.get (clazz);
318 if (osc == null)
319 classLookupTable.put (clazz, this);
320 superClass = lookupForClassObject (clazz.getSuperclass ());
321 calculateOffsets ();
322 }
323
324 void setSuperclass (ObjectStreamClass osc)
325 {
326 superClass = osc;
327 }
328
329
330 void calculateOffsets ()
331 {
332 int i;
333 ObjectStreamField field;
334 primFieldSize = 0;
335 int fcount = fields.length;
336 for (i = 0; i < fcount; ++ i)
337 {
338 field = fields[i];
339
340 if (! field.isPrimitive ())
341 break;
342
343 field.setOffset (primFieldSize);
344 switch (field.getTypeCode ())
345 {
346 case 'B':
347 case 'Z':
348 ++ primFieldSize;
349 break;
350 case 'C':
351 case 'S':
352 primFieldSize += 2;
353 break;
354 case 'I':
355 case 'F':
356 primFieldSize += 4;
357 break;
358 case 'D':
359 case 'J':
360 primFieldSize += 8;
361 break;
362 }
363 }
364
365 for (objectFieldCount = 0; i < fcount; ++ i)
366 fields[i].setOffset (objectFieldCount++);
367 }
368
369
370 private ObjectStreamClass (Class cl)
371 {
372 uid = 0;
373 flags = 0;
374 isProxyClass = Proxy.isProxyClass (cl);
375
376 clazz = cl;
377 name = cl.getName ();
378 setFlags (cl);
379 setFields (cl);
380 // to those class nonserializable, its uid field is 0
381 if ( (Serializable.class).isAssignableFrom (cl) && !isProxyClass)
382 uid = getClassUID (cl);
383 superClass = lookup (cl.getSuperclass ());
384 }
385
386
387 // Sets bits in flags according to features of CL.
388 private void setFlags (Class cl)
389 {
390 if ((java.io.Externalizable.class).isAssignableFrom (cl))
391 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
392 else if ((java.io.Serializable.class).isAssignableFrom (cl))
393 // only set this bit if CL is NOT Externalizable
394 flags |= ObjectStreamConstants.SC_SERIALIZABLE;
395
396 try
397 {
398 Method writeMethod = cl.getDeclaredMethod ("writeObject",
399 writeMethodArgTypes);
400 int modifiers = writeMethod.getModifiers ();
401
402 if (writeMethod.getReturnType () == Void.TYPE
403 && Modifier.isPrivate (modifiers)
404 && !Modifier.isStatic (modifiers))
405 flags |= ObjectStreamConstants.SC_WRITE_METHOD;
406 }
407 catch (NoSuchMethodException oh_well)
408 {}
409 }
410
411
412 // Sets fields to be a sorted array of the serializable fields of
413 // clazz.
414 private void setFields (Class cl)
415 {
416 if (! isSerializable () || isExternalizable ())
417 {
418 fields = NO_FIELDS;
419 return;
420 }
421
422 try
423 {
424 Field serialPersistentFields
425 = cl.getDeclaredField ("serialPersistentFields");
426 serialPersistentFields.setAccessible(true);
427 int modifiers = serialPersistentFields.getModifiers ();
428
429 if (Modifier.isStatic (modifiers)
430 && Modifier.isFinal (modifiers)
431 && Modifier.isPrivate (modifiers))
432 {
433 fields = getSerialPersistentFields (cl);
434 Arrays.sort (fields);
435 calculateOffsets ();
436 return;
437 }
438 }
439 catch (NoSuchFieldException ignore)
440 {}
441
442 int num_good_fields = 0;
443 Field[] all_fields = cl.getDeclaredFields ();
444
445 int modifiers;
446 // set non-serializable fields to null in all_fields
447 for (int i=0; i < all_fields.length; i++)
448 {
449 modifiers = all_fields[i].getModifiers ();
450 if (Modifier.isTransient (modifiers)
451 || Modifier.isStatic (modifiers))
452 all_fields[i] = null;
453 else
454 num_good_fields++;
455 }
456
457 // make a copy of serializable (non-null) fields
458 fields = new ObjectStreamField[ num_good_fields ];
459 for (int from=0, to=0; from < all_fields.length; from++)
460 if (all_fields[from] != null)
461 {
462 Field f = all_fields[from];
463 fields[to] = new ObjectStreamField (f.getName (), f.getType ());
464 to++;
465 }
466
467 Arrays.sort (fields);
468 calculateOffsets ();
469 }
470
471 // Returns the serial version UID defined by class, or if that
472 // isn't present, calculates value of serial version UID.
473 private long getClassUID (Class cl)
474 {
475 try
476 {
477 // Use getDeclaredField rather than getField, since serialVersionUID
478 // may not be public AND we only want the serialVersionUID of this
479 // class, not a superclass or interface.
480 Field suid = cl.getDeclaredField ("serialVersionUID");
481 suid.setAccessible(true);
482 int modifiers = suid.getModifiers ();
483
484 if (Modifier.isStatic (modifiers)
485 && Modifier.isFinal (modifiers)
486 && suid.getType() == Long.TYPE)
487 return suid.getLong (null);
488 }
489 catch (NoSuchFieldException ignore)
490 {}
491 catch (IllegalAccessException ignore)
492 {}
493
494 // cl didn't define serialVersionUID, so we have to compute it
495 try
496 {
497 MessageDigest md;
498 try
499 {
500 md = MessageDigest.getInstance ("SHA");
501 }
502 catch (NoSuchAlgorithmException e)
503 {
504 // If a provider already provides SHA, use it; otherwise, use this.
505 Gnu gnuProvider = new Gnu();
506 Security.addProvider(gnuProvider);
507 md = MessageDigest.getInstance ("SHA");
508 }
509
510 DigestOutputStream digest_out =
511 new DigestOutputStream (nullOutputStream, md);
512 DataOutputStream data_out = new DataOutputStream (digest_out);
513
514 data_out.writeUTF (cl.getName ());
515
516 int modifiers = cl.getModifiers ();
517 // just look at interesting bits
518 modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
519 | Modifier.INTERFACE | Modifier.PUBLIC);
520 data_out.writeInt (modifiers);
521
522 // Pretend that an array has no interfaces, because when array
523 // serialization was defined (JDK 1.1), arrays didn't have it.
524 if (! cl.isArray ())
525 {
526 Class[] interfaces = cl.getInterfaces ();
527 Arrays.sort (interfaces, interfaceComparator);
528 for (int i=0; i < interfaces.length; i++)
529 data_out.writeUTF (interfaces[i].getName ());
530 }
531
532 Field field;
533 Field[] fields = cl.getDeclaredFields ();
534 Arrays.sort (fields, memberComparator);
535 for (int i=0; i < fields.length; i++)
536 {
537 field = fields[i];
538 modifiers = field.getModifiers ();
539 if (Modifier.isPrivate (modifiers)
540 && (Modifier.isStatic (modifiers)
541 || Modifier.isTransient (modifiers)))
542 continue;
543
544 data_out.writeUTF (field.getName ());
545 data_out.writeInt (modifiers);
546 data_out.writeUTF (TypeSignature.getEncodingOfClass (field.getType ()));
547 }
548
549 // write class initializer method if present
550 if (VMObjectStreamClass.hasClassInitializer (cl))
551 {
552 data_out.writeUTF ("<clinit>");
553 data_out.writeInt (Modifier.STATIC);
554 data_out.writeUTF ("()V");
555 }
556
557 Constructor constructor;
558 Constructor[] constructors = cl.getDeclaredConstructors ();
559 Arrays.sort (constructors, memberComparator);
560 for (int i=0; i < constructors.length; i++)
561 {
562 constructor = constructors[i];
563 modifiers = constructor.getModifiers ();
564 if (Modifier.isPrivate (modifiers))
565 continue;
566
567 data_out.writeUTF ("<init>");
568 data_out.writeInt (modifiers);
569
570 // the replacement of '/' with '.' was needed to make computed
571 // SUID's agree with those computed by JDK
572 data_out.writeUTF (
573 TypeSignature.getEncodingOfConstructor (constructor).replace ('/','.'));
574 }
575
576 Method method;
577 Method[] methods = cl.getDeclaredMethods ();
578 Arrays.sort (methods, memberComparator);
579 for (int i=0; i < methods.length; i++)
580 {
581 method = methods[i];
582 modifiers = method.getModifiers ();
583 if (Modifier.isPrivate (modifiers))
584 continue;
585
586 data_out.writeUTF (method.getName ());
587 data_out.writeInt (modifiers);
588
589 // the replacement of '/' with '.' was needed to make computed
590 // SUID's agree with those computed by JDK
591 data_out.writeUTF (
592 TypeSignature.getEncodingOfMethod (method).replace ('/', '.'));
593 }
594
595 data_out.close ();
596 byte[] sha = md.digest ();
597 long result = 0;
598 int len = sha.length < 8 ? sha.length : 8;
599 for (int i=0; i < len; i++)
600 result += (long)(sha[i] & 0xFF) << (8 * i);
601
602 return result;
603 }
604 catch (NoSuchAlgorithmException e)
605 {
606 throw new RuntimeException ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
607 + cl.getName (), e);
608 }
609 catch (IOException ioe)
610 {
611 throw new RuntimeException (ioe);
612 }
613 }
614
615 // Returns the value of CLAZZ's private static final field named
616 // `serialPersistentFields'.
617 private ObjectStreamField[] getSerialPersistentFields (Class clazz)
618 {
619 ObjectStreamField[] o = null;
620 try
621 {
622 // Use getDeclaredField rather than getField for the same reason
623 // as above in getDefinedSUID.
624 Field f = clazz.getDeclaredField ("getSerialPersistentFields");
625 f.setAccessible(true);
626 o = (ObjectStreamField[])f.get (null);
627 }
628 catch (java.lang.NoSuchFieldException e)
629 {
630 }
631 catch (java.lang.IllegalAccessException e)
632 {
633 }
634
635 return o;
636 }
637
638 public static final ObjectStreamField[] NO_FIELDS = {};
639
640 private static Hashtable classLookupTable = new Hashtable ();
641 private static final NullOutputStream nullOutputStream = new NullOutputStream ();
642 private static final Comparator interfaceComparator = new InterfaceComparator ();
643 private static final Comparator memberComparator = new MemberComparator ();
644 private static final
645 Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
646
647 private ObjectStreamClass superClass;
648 private Class clazz;
649 private String name;
650 private long uid;
651 private byte flags;
652
653 // this field is package protected so that ObjectInputStream and
654 // ObjectOutputStream can access it directly
655 ObjectStreamField[] fields;
656
657 // these are accessed by ObjectIn/OutputStream
658 int primFieldSize = -1; // -1 if not yet calculated
659 int objectFieldCount;
660
661 boolean isProxyClass = false;
662
663 // This is probably not necessary because this class is special cased already
664 // but it will avoid showing up as a discrepancy when comparing SUIDs.
665 private static final long serialVersionUID = -6120832682080437368L;
666
667}
668
669
670// interfaces are compared only by name
671class InterfaceComparator implements Comparator
672{
673 public int compare (Object o1, Object o2)
674 {
675 return ((Class)o1).getName ().compareTo (((Class)o2).getName ());
676 }
677}
678
679
680// Members (Methods and Constructors) are compared first by name,
681// conflicts are resolved by comparing type signatures
682class MemberComparator implements Comparator
683{
684 public int compare (Object o1, Object o2)
685 {
686 Member m1 = (Member)o1;
687 Member m2 = (Member)o2;
688
689 int comp = m1.getName ().compareTo (m2.getName ());
690
691 if (comp == 0)
692 return TypeSignature.getEncodingOfMember (m1).
693 compareTo (TypeSignature.getEncodingOfMember (m2));
694 else
695 return comp;
696 }
697}
Note: See TracBrowser for help on using the repository browser.