1 /* ObjectInputStream.java -- Class used to read serialized objects 2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008 3 Free Software Foundation, Inc. 4 5 This file is part of GNU Classpath. 6 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) 10 any later version. 11 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. 16 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., 51 Franklin Street, Fifth Floor, Boston, MA 20 02110-1301 USA. 21 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 25 combination. 26 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. */ 38 39 40 package java.io; 41 42 import gnu.classpath.Pair; 43 import gnu.classpath.VMStackWalker; 44 45 import java.lang.reflect.Array; 46 import java.lang.reflect.Constructor; 47 import java.lang.reflect.Field; 48 import java.lang.reflect.InvocationTargetException; 49 import java.lang.reflect.Method; 50 import java.lang.reflect.Modifier; 51 import java.lang.reflect.Proxy; 52 import java.security.AccessController; 53 import java.security.PrivilegedAction; 54 import java.util.HashMap; 55 import java.util.Hashtable; 56 import java.util.Iterator; 57 import java.util.Map; 58 import java.util.TreeSet; 59 60 /** 61 * @author Tom Tromey (tromey@redhat.com) 62 * @author Jeroen Frijters (jeroen@frijters.net) 63 * @author Guilhem Lavaux (guilhem@kaffe.org) 64 * @author Michael Koch (konqueror@gmx.de) 65 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 66 */ 67 public class ObjectInputStream extends InputStream 68 implements ObjectInput, ObjectStreamConstants 69 { 70 /** 71 * Creates a new <code>ObjectInputStream</code> that will do all of 72 * its reading from <code>in</code>. This method also checks 73 * the stream by reading the header information (stream magic number 74 * and stream version). 75 * 76 * @exception IOException Reading stream header from underlying 77 * stream cannot be completed. 78 * 79 * @exception StreamCorruptedException An invalid stream magic 80 * number or stream version was read from the stream. 81 * 82 * @see #readStreamHeader() 83 */ ObjectInputStream(InputStream in)84 public ObjectInputStream(InputStream in) 85 throws IOException, StreamCorruptedException 86 { 87 if (DEBUG) 88 { 89 String val = System.getProperty("gcj.dumpobjects"); 90 if (dump == false && val != null && !val.equals("")) 91 { 92 dump = true; 93 System.out.println ("Serialization debugging enabled"); 94 } 95 else if (dump == true && (val == null || val.equals(""))) 96 { 97 dump = false; 98 System.out.println ("Serialization debugging disabled"); 99 } 100 } 101 102 this.resolveEnabled = false; 103 this.blockDataPosition = 0; 104 this.blockDataBytes = 0; 105 this.blockData = new byte[BUFFER_SIZE]; 106 this.blockDataInput = new DataInputStream(this); 107 this.realInputStream = new DataInputStream(in); 108 this.nextOID = baseWireHandle; 109 handles = new HashMap<Integer,Pair<Boolean,Object>>(); 110 this.classLookupTable = new Hashtable<Class,ObjectStreamClass>(); 111 setBlockDataMode(true); 112 readStreamHeader(); 113 } 114 115 116 /** 117 * Returns the next deserialized object read from the underlying stream. 118 * 119 * This method can be overriden by a class by implementing 120 * <code>private void readObject (ObjectInputStream)</code>. 121 * 122 * If an exception is thrown from this method, the stream is left in 123 * an undefined state. This method can also throw Errors and 124 * RuntimeExceptions if caused by existing readResolve() user code. 125 * 126 * @return The object read from the underlying stream. 127 * 128 * @exception ClassNotFoundException The class that an object being 129 * read in belongs to cannot be found. 130 * 131 * @exception IOException Exception from underlying 132 * <code>InputStream</code>. 133 */ readObject()134 public final Object readObject() 135 throws ClassNotFoundException, IOException 136 { 137 return readObject(true); 138 } 139 140 /** 141 * <p> 142 * Returns the next deserialized object read from the 143 * underlying stream in an unshared manner. Any object 144 * returned by this method will not be returned by 145 * subsequent calls to either this method or {@link #readObject()}. 146 * </p> 147 * <p> 148 * This behaviour is achieved by: 149 * </p> 150 * <ul> 151 * <li>Marking the handles created by successful calls to this 152 * method, so that future calls to {@link #readObject()} or 153 * {@link #readUnshared()} will throw an {@link ObjectStreamException} 154 * rather than returning the same object reference.</li> 155 * <li>Throwing an {@link ObjectStreamException} if the next 156 * element in the stream is a reference to an earlier object.</li> 157 * </ul> 158 * 159 * @return a reference to the deserialized object. 160 * @throws ClassNotFoundException if the class of the object being 161 * deserialized can not be found. 162 * @throws StreamCorruptedException if information in the stream 163 * is inconsistent. 164 * @throws ObjectStreamException if the next object has already been 165 * returned by an earlier call to this 166 * method or {@link #readObject()}. 167 * @throws OptionalDataException if primitive data occurs next in the stream. 168 * @throws IOException if an I/O error occurs from the stream. 169 * @since 1.4 170 * @see #readObject() 171 */ readUnshared()172 public Object readUnshared() 173 throws IOException, ClassNotFoundException 174 { 175 return readObject(false); 176 } 177 178 /** 179 * Returns the next deserialized object read from the underlying stream. 180 * 181 * This method can be overriden by a class by implementing 182 * <code>private void readObject (ObjectInputStream)</code>. 183 * 184 * If an exception is thrown from this method, the stream is left in 185 * an undefined state. This method can also throw Errors and 186 * RuntimeExceptions if caused by existing readResolve() user code. 187 * 188 * @param shared true if handles created by this call should be shared 189 * with later calls. 190 * @return The object read from the underlying stream. 191 * 192 * @exception ClassNotFoundException The class that an object being 193 * read in belongs to cannot be found. 194 * 195 * @exception IOException Exception from underlying 196 * <code>InputStream</code>. 197 */ readObject(boolean shared)198 private final Object readObject(boolean shared) 199 throws ClassNotFoundException, IOException 200 { 201 if (this.useSubclassMethod) 202 return readObjectOverride(); 203 204 Object ret_val; 205 boolean old_mode = setBlockDataMode(false); 206 byte marker = this.realInputStream.readByte(); 207 208 if (DEBUG) 209 depth += 2; 210 211 if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " "); 212 213 try 214 { 215 ret_val = parseContent(marker, shared); 216 } 217 finally 218 { 219 setBlockDataMode(old_mode); 220 if (DEBUG) 221 depth -= 2; 222 } 223 224 return ret_val; 225 } 226 227 /** 228 * Handles a content block within the stream, which begins with a marker 229 * byte indicating its type. 230 * 231 * @param marker the byte marker. 232 * @param shared true if handles created by this call should be shared 233 * with later calls. 234 * @return an object which represents the parsed content. 235 * @throws ClassNotFoundException if the class of an object being 236 * read in cannot be found. 237 * @throws IOException if invalid data occurs or one is thrown by the 238 * underlying <code>InputStream</code>. 239 */ parseContent(byte marker, boolean shared)240 private Object parseContent(byte marker, boolean shared) 241 throws ClassNotFoundException, IOException 242 { 243 Object ret_val; 244 boolean is_consumed = false; 245 246 switch (marker) 247 { 248 case TC_ENDBLOCKDATA: 249 { 250 ret_val = null; 251 is_consumed = true; 252 break; 253 } 254 255 case TC_BLOCKDATA: 256 case TC_BLOCKDATALONG: 257 { 258 if (marker == TC_BLOCKDATALONG) 259 { if(dump) dumpElementln("BLOCKDATALONG"); } 260 else 261 { if(dump) dumpElementln("BLOCKDATA"); } 262 readNextBlock(marker); 263 } 264 265 case TC_NULL: 266 { 267 if(dump) dumpElementln("NULL"); 268 ret_val = null; 269 break; 270 } 271 272 case TC_REFERENCE: 273 { 274 if(dump) dumpElement("REFERENCE "); 275 int oid = realInputStream.readInt(); 276 if(dump) dumpElementln(Integer.toHexString(oid)); 277 ret_val = lookupHandle(oid); 278 if (!shared) 279 throw new 280 InvalidObjectException("References can not be read unshared."); 281 break; 282 } 283 284 case TC_CLASS: 285 { 286 if(dump) dumpElementln("CLASS"); 287 ObjectStreamClass osc = (ObjectStreamClass)readObject(); 288 Class clazz = osc.forClass(); 289 assignNewHandle(clazz,shared); 290 ret_val = clazz; 291 break; 292 } 293 294 case TC_PROXYCLASSDESC: 295 { 296 if(dump) dumpElementln("PROXYCLASS"); 297 298 /* GCJ LOCAL */ 299 // The grammar at this point is 300 // TC_PROXYCLASSDESC newHandle proxyClassDescInfo 301 // i.e. we have to assign the handle immediately after 302 // reading the marker. 303 int handle = assignNewHandle("Dummy proxy",shared); 304 /* END GCJ LOCAL */ 305 306 int n_intf = this.realInputStream.readInt(); 307 String[] intfs = new String[n_intf]; 308 for (int i = 0; i < n_intf; i++) 309 { 310 intfs[i] = this.realInputStream.readUTF(); 311 } 312 313 boolean oldmode = setBlockDataMode(true); 314 Class cl = resolveProxyClass(intfs); 315 setBlockDataMode(oldmode); 316 317 ObjectStreamClass osc = lookupClass(cl); 318 if (osc.firstNonSerializableParentConstructor == null) 319 { 320 osc.realClassIsSerializable = true; 321 osc.fields = osc.fieldMapping = new ObjectStreamField[0]; 322 try 323 { 324 osc.firstNonSerializableParentConstructor = 325 Object.class.getConstructor(new Class[0]); 326 } 327 catch (NoSuchMethodException x) 328 { 329 throw (InternalError) 330 new InternalError("Object ctor missing").initCause(x); 331 } 332 } 333 /* GCJ LOCAL */ 334 rememberHandle(osc,shared,handle); 335 /* END GCJ LOCAL */ 336 337 if (!is_consumed) 338 { 339 byte b = this.realInputStream.readByte(); 340 if (b != TC_ENDBLOCKDATA) 341 throw new IOException("Data annotated to class was not consumed." + b); 342 } 343 else 344 is_consumed = false; 345 ObjectStreamClass superosc = (ObjectStreamClass)readObject(); 346 osc.setSuperclass(superosc); 347 ret_val = osc; 348 break; 349 } 350 351 case TC_CLASSDESC: 352 { 353 ObjectStreamClass osc = readClassDescriptor(); 354 355 if (!is_consumed) 356 { 357 byte b = this.realInputStream.readByte(); 358 if (b != TC_ENDBLOCKDATA) 359 throw new IOException("Data annotated to class was not consumed." + b); 360 } 361 else 362 is_consumed = false; 363 364 osc.setSuperclass ((ObjectStreamClass)readObject()); 365 ret_val = osc; 366 break; 367 } 368 369 case TC_STRING: 370 { 371 if(dump) dumpElement("STRING="); 372 String s = this.realInputStream.readUTF(); 373 if(dump) dumpElementln(s); 374 ret_val = processResolution(null, s, assignNewHandle(s,shared), 375 shared); 376 break; 377 } 378 379 case TC_LONGSTRING: 380 { 381 if(dump) dumpElement("STRING="); 382 String s = this.realInputStream.readUTFLong(); 383 if(dump) dumpElementln(s); 384 ret_val = processResolution(null, s, assignNewHandle(s,shared), 385 shared); 386 break; 387 } 388 389 case TC_ARRAY: 390 { 391 if(dump) dumpElementln("ARRAY"); 392 ObjectStreamClass osc = (ObjectStreamClass)readObject(); 393 Class componentType = osc.forClass().getComponentType(); 394 if(dump) dumpElement("ARRAY LENGTH="); 395 int length = this.realInputStream.readInt(); 396 if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); 397 Object array = Array.newInstance(componentType, length); 398 int handle = assignNewHandle(array,shared); 399 readArrayElements(array, componentType); 400 if(dump) 401 for (int i = 0, len = Array.getLength(array); i < len; i++) 402 dumpElementln(" ELEMENT[" + i + "]=", Array.get(array, i)); 403 ret_val = processResolution(null, array, handle, shared); 404 break; 405 } 406 407 case TC_OBJECT: 408 { 409 if(dump) dumpElementln("OBJECT"); 410 ObjectStreamClass osc = (ObjectStreamClass)readObject(); 411 Class clazz = osc.forClass(); 412 413 if (!osc.realClassIsSerializable) 414 throw new NotSerializableException 415 (clazz + " is not Serializable, and thus cannot be deserialized."); 416 417 if (osc.realClassIsExternalizable) 418 { 419 Externalizable obj = osc.newInstance(); 420 421 int handle = assignNewHandle(obj,shared); 422 423 boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); 424 425 boolean oldmode = this.readDataFromBlock; 426 if (read_from_blocks) 427 setBlockDataMode(true); 428 429 obj.readExternal(this); 430 431 if (read_from_blocks) 432 { 433 setBlockDataMode(oldmode); 434 if (!oldmode) 435 if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) 436 throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); 437 } 438 439 ret_val = processResolution(osc, obj, handle,shared); 440 break; 441 442 } // end if (osc.realClassIsExternalizable) 443 444 Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); 445 446 int handle = assignNewHandle(obj,shared); 447 Object prevObject = this.currentObject; 448 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; 449 TreeSet<ValidatorAndPriority> prevObjectValidators = 450 this.currentObjectValidators; 451 452 this.currentObject = obj; 453 this.currentObjectValidators = null; 454 ObjectStreamClass[] hierarchy = hierarchy(clazz); 455 456 for (int i = 0; i < hierarchy.length; i++) 457 { 458 this.currentObjectStreamClass = hierarchy[i]; 459 if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ()); 460 461 // XXX: should initialize fields in classes in the hierarchy 462 // that aren't in the stream 463 // should skip over classes in the stream that aren't in the 464 // real classes hierarchy 465 466 Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod; 467 if (readObjectMethod != null) 468 { 469 fieldsAlreadyRead = false; 470 boolean oldmode = setBlockDataMode(true); 471 callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj); 472 setBlockDataMode(oldmode); 473 } 474 else 475 { 476 readFields(obj, currentObjectStreamClass); 477 } 478 479 if (this.currentObjectStreamClass.hasWriteMethod()) 480 { 481 if(dump) dumpElement("ENDBLOCKDATA? "); 482 try 483 { 484 /* Read blocks until an end marker */ 485 byte writeMarker = this.realInputStream.readByte(); 486 while (writeMarker != TC_ENDBLOCKDATA) 487 { 488 parseContent(writeMarker, shared); 489 writeMarker = this.realInputStream.readByte(); 490 } 491 if(dump) dumpElementln("yes"); 492 } 493 catch (EOFException e) 494 { 495 throw (IOException) new IOException 496 ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e); 497 } 498 } 499 } 500 501 this.currentObject = prevObject; 502 this.currentObjectStreamClass = prevObjectStreamClass; 503 ret_val = processResolution(osc, obj, handle, shared); 504 if (currentObjectValidators != null) 505 invokeValidators(); 506 this.currentObjectValidators = prevObjectValidators; 507 508 break; 509 } 510 511 case TC_RESET: 512 if(dump) dumpElementln("RESET"); 513 clearHandles(); 514 ret_val = readObject(); 515 break; 516 517 case TC_EXCEPTION: 518 { 519 if(dump) dumpElement("EXCEPTION="); 520 Exception e = (Exception)readObject(); 521 if(dump) dumpElementln(e.toString()); 522 clearHandles(); 523 throw new WriteAbortedException("Exception thrown during writing of stream", e); 524 } 525 526 case TC_ENUM: 527 { 528 /* TC_ENUM classDesc newHandle enumConstantName */ 529 if (dump) 530 dumpElementln("ENUM="); 531 ObjectStreamClass osc = (ObjectStreamClass) readObject(); 532 int enumHandle = assignNewHandle(null, shared); 533 String constantName = (String) readObject(); 534 if (dump) 535 dumpElementln("CONSTANT NAME = " + constantName); 536 Class clazz = osc.forClass(); 537 Enum instance = Enum.valueOf(clazz, constantName); 538 rememberHandle(instance, shared, enumHandle); 539 ret_val = instance; 540 break; 541 } 542 543 default: 544 throw new IOException("Unknown marker on stream: " + marker); 545 } 546 return ret_val; 547 } 548 549 /** 550 * This method makes a partial check of types for the fields 551 * contained given in arguments. It checks primitive types of 552 * fields1 against non primitive types of fields2. This method 553 * assumes the two lists has already been sorted according to 554 * the Java specification. 555 * 556 * @param name Name of the class owning the given fields. 557 * @param fields1 First list to check. 558 * @param fields2 Second list to check. 559 * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present 560 * in the non primitive part in fields2. 561 */ checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2)562 private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2) 563 throws InvalidClassException 564 { 565 int nonPrimitive = 0; 566 567 for (nonPrimitive = 0; 568 nonPrimitive < fields1.length 569 && fields1[nonPrimitive].isPrimitive(); nonPrimitive++) 570 { 571 } 572 573 if (nonPrimitive == fields1.length) 574 return; 575 576 int i = 0; 577 ObjectStreamField f1; 578 ObjectStreamField f2; 579 580 while (i < fields2.length 581 && nonPrimitive < fields1.length) 582 { 583 f1 = fields1[nonPrimitive]; 584 f2 = fields2[i]; 585 586 if (!f2.isPrimitive()) 587 break; 588 589 int compVal = f1.getName().compareTo (f2.getName()); 590 591 if (compVal < 0) 592 { 593 nonPrimitive++; 594 } 595 else if (compVal > 0) 596 { 597 i++; 598 } 599 else 600 { 601 throw new InvalidClassException 602 ("invalid field type for " + f2.getName() + 603 " in class " + name); 604 } 605 } 606 } 607 608 /** 609 * This method reads a class descriptor from the real input stream 610 * and use these data to create a new instance of ObjectStreamClass. 611 * Fields are sorted and ordered for the real read which occurs for 612 * each instance of the described class. Be aware that if you call that 613 * method you must ensure that the stream is synchronized, in the other 614 * case it may be completely desynchronized. 615 * 616 * @return A new instance of ObjectStreamClass containing the freshly 617 * created descriptor. 618 * @throws ClassNotFoundException if the required class to build the 619 * descriptor has not been found in the system. 620 * @throws IOException An input/output error occured. 621 * @throws InvalidClassException If there was a compatibility problem 622 * between the class present in the system and the serialized class. 623 */ readClassDescriptor()624 protected ObjectStreamClass readClassDescriptor() 625 throws ClassNotFoundException, IOException 626 { 627 if(dump) dumpElement("CLASSDESC NAME="); 628 String name = this.realInputStream.readUTF(); 629 if(dump) dumpElement(name + "; UID="); 630 long uid = this.realInputStream.readLong (); 631 if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS="); 632 byte flags = this.realInputStream.readByte (); 633 if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT="); 634 short field_count = this.realInputStream.readShort(); 635 if(dump) dumpElementln(Short.toString(field_count)); 636 ObjectStreamField[] fields = new ObjectStreamField[field_count]; 637 ObjectStreamClass osc = new ObjectStreamClass(name, uid, 638 flags, fields); 639 assignNewHandle(osc,true); 640 641 for (int i = 0; i < field_count; i++) 642 { 643 if(dump) dumpElement(" TYPE CODE="); 644 char type_code = (char)this.realInputStream.readByte(); 645 if(dump) dumpElement(type_code + "; FIELD NAME="); 646 String field_name = this.realInputStream.readUTF(); 647 if(dump) dumpElementln(field_name); 648 String class_name; 649 650 // If the type code is an array or an object we must 651 // decode a String here. In the other case we convert 652 // the type code and pass it to ObjectStreamField. 653 // Type codes are decoded by gnu.java.lang.reflect.TypeSignature. 654 if (type_code == 'L' || type_code == '[') 655 class_name = (String)readObject(); 656 else 657 class_name = String.valueOf(type_code); 658 659 fields[i] = 660 new ObjectStreamField(field_name, class_name); 661 } 662 663 /* Now that fields have been read we may resolve the class 664 * (and read annotation if needed). */ 665 Class clazz = resolveClass(osc); 666 ClassLoader loader = clazz.getClassLoader(); 667 for (int i = 0; i < field_count; i++) 668 { 669 fields[i].resolveType(loader); 670 } 671 boolean oldmode = setBlockDataMode(true); 672 osc.setClass(clazz, lookupClass(clazz.getSuperclass())); 673 classLookupTable.put(clazz, osc); 674 setBlockDataMode(oldmode); 675 676 // find the first non-serializable class in clazz's inheritance hierarchy 677 Class first_nonserial = clazz.getSuperclass(); 678 // Maybe it is a primitive class, those don't have a super class, 679 // or Object itself. Otherwise we can keep getting the superclass 680 // till we hit the Object class, or some other non-serializable class. 681 682 if (first_nonserial == null) 683 first_nonserial = clazz; 684 else 685 while (Serializable.class.isAssignableFrom(first_nonserial)) 686 first_nonserial = first_nonserial.getSuperclass(); 687 688 final Class local_constructor_class = first_nonserial; 689 690 osc.firstNonSerializableParentConstructor = 691 (Constructor)AccessController.doPrivileged(new PrivilegedAction() 692 { 693 public Object run() 694 { 695 try 696 { 697 Constructor c = local_constructor_class. 698 getDeclaredConstructor(new Class[0]); 699 if (Modifier.isPrivate(c.getModifiers())) 700 return null; 701 return c; 702 } 703 catch (NoSuchMethodException e) 704 { 705 // error will be reported later, in newObject() 706 return null; 707 } 708 } 709 }); 710 711 osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz); 712 osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz); 713 714 ObjectStreamField[] stream_fields = osc.fields; 715 ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields; 716 ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)]; 717 718 int stream_idx = 0; 719 int real_idx = 0; 720 int map_idx = 0; 721 722 /* 723 * Check that there is no type inconsistencies between the lists. 724 * A special checking must be done for the two groups: primitive types and 725 * not primitive types. 726 */ 727 checkTypeConsistency(name, real_fields, stream_fields); 728 checkTypeConsistency(name, stream_fields, real_fields); 729 730 731 while (stream_idx < stream_fields.length 732 || real_idx < real_fields.length) 733 { 734 ObjectStreamField stream_field = null; 735 ObjectStreamField real_field = null; 736 737 if (stream_idx == stream_fields.length) 738 { 739 real_field = real_fields[real_idx++]; 740 } 741 else if (real_idx == real_fields.length) 742 { 743 stream_field = stream_fields[stream_idx++]; 744 } 745 else 746 { 747 int comp_val = 748 real_fields[real_idx].compareTo (stream_fields[stream_idx]); 749 750 if (comp_val < 0) 751 { 752 real_field = real_fields[real_idx++]; 753 } 754 else if (comp_val > 0) 755 { 756 stream_field = stream_fields[stream_idx++]; 757 } 758 else 759 { 760 stream_field = stream_fields[stream_idx++]; 761 real_field = real_fields[real_idx++]; 762 if (stream_field.getType() != real_field.getType()) 763 throw new InvalidClassException 764 ("invalid field type for " + real_field.getName() + 765 " in class " + name); 766 } 767 } 768 769 /* If some of stream_fields does not correspond to any of real_fields, 770 * or the opposite, then fieldmapping will go short. 771 */ 772 if (map_idx == fieldmapping.length) 773 { 774 ObjectStreamField[] newfieldmapping = 775 new ObjectStreamField[fieldmapping.length + 2]; 776 System.arraycopy(fieldmapping, 0, 777 newfieldmapping, 0, fieldmapping.length); 778 fieldmapping = newfieldmapping; 779 } 780 fieldmapping[map_idx++] = stream_field; 781 fieldmapping[map_idx++] = real_field; 782 } 783 osc.fieldMapping = fieldmapping; 784 785 return osc; 786 } 787 788 /** 789 * Reads the current objects non-transient, non-static fields from 790 * the current class from the underlying output stream. 791 * 792 * This method is intended to be called from within a object's 793 * <code>private void readObject (ObjectInputStream)</code> 794 * method. 795 * 796 * @exception ClassNotFoundException The class that an object being 797 * read in belongs to cannot be found. 798 * 799 * @exception NotActiveException This method was called from a 800 * context other than from the current object's and current class's 801 * <code>private void readObject (ObjectInputStream)</code> 802 * method. 803 * 804 * @exception IOException Exception from underlying 805 * <code>OutputStream</code>. 806 */ defaultReadObject()807 public void defaultReadObject() 808 throws ClassNotFoundException, IOException, NotActiveException 809 { 810 if (this.currentObject == null || this.currentObjectStreamClass == null) 811 throw new NotActiveException("defaultReadObject called by non-active" 812 + " class and/or object"); 813 814 if (fieldsAlreadyRead) 815 throw new NotActiveException("defaultReadObject called but fields " 816 + "already read from stream (by " 817 + "defaultReadObject or readFields)"); 818 819 boolean oldmode = setBlockDataMode(false); 820 readFields(this.currentObject, this.currentObjectStreamClass); 821 setBlockDataMode(oldmode); 822 823 fieldsAlreadyRead = true; 824 } 825 826 827 /** 828 * Registers a <code>ObjectInputValidation</code> to be carried out 829 * on the object graph currently being deserialized before it is 830 * returned to the original caller of <code>readObject ()</code>. 831 * The order of validation for multiple 832 * <code>ObjectInputValidation</code>s can be controled using 833 * <code>priority</code>. Validators with higher priorities are 834 * called first. 835 * 836 * @see java.io.ObjectInputValidation 837 * 838 * @exception InvalidObjectException <code>validator</code> is 839 * <code>null</code> 840 * 841 * @exception NotActiveException an attempt was made to add a 842 * validator outside of the <code>readObject</code> method of the 843 * object currently being deserialized 844 */ registerValidation(ObjectInputValidation validator, int priority)845 public void registerValidation(ObjectInputValidation validator, 846 int priority) 847 throws InvalidObjectException, NotActiveException 848 { 849 if (this.currentObject == null || this.currentObjectStreamClass == null) 850 throw new NotActiveException("registerValidation called by non-active " 851 + "class and/or object"); 852 853 if (validator == null) 854 throw new InvalidObjectException("attempt to add a null " 855 + "ObjectInputValidation object"); 856 857 if (currentObjectValidators == null) 858 currentObjectValidators = new TreeSet<ValidatorAndPriority>(); 859 860 currentObjectValidators.add(new ValidatorAndPriority(validator, priority)); 861 } 862 863 864 /** 865 * Called when a class is being deserialized. This is a hook to 866 * allow subclasses to read in information written by the 867 * <code>annotateClass (Class)</code> method of an 868 * <code>ObjectOutputStream</code>. 869 * 870 * This implementation looks up the active call stack for a 871 * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found, 872 * it is used to load the class associated with <code>osc</code>, 873 * otherwise, the default system <code>ClassLoader</code> is used. 874 * 875 * @exception IOException Exception from underlying 876 * <code>OutputStream</code>. 877 * 878 * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) 879 */ resolveClass(ObjectStreamClass osc)880 protected Class<?> resolveClass(ObjectStreamClass osc) 881 throws ClassNotFoundException, IOException 882 { 883 String name = osc.getName(); 884 try 885 { 886 return Class.forName(name, true, currentLoader()); 887 } 888 catch(ClassNotFoundException x) 889 { 890 if (name.equals("void")) 891 return Void.TYPE; 892 else if (name.equals("boolean")) 893 return Boolean.TYPE; 894 else if (name.equals("byte")) 895 return Byte.TYPE; 896 else if (name.equals("char")) 897 return Character.TYPE; 898 else if (name.equals("short")) 899 return Short.TYPE; 900 else if (name.equals("int")) 901 return Integer.TYPE; 902 else if (name.equals("long")) 903 return Long.TYPE; 904 else if (name.equals("float")) 905 return Float.TYPE; 906 else if (name.equals("double")) 907 return Double.TYPE; 908 else 909 throw x; 910 } 911 } 912 913 /** 914 * Returns the most recent user defined ClassLoader on the execution stack 915 * or null if none is found. 916 */ currentLoader()917 private ClassLoader currentLoader() 918 { 919 return VMStackWalker.firstNonNullClassLoader(); 920 } 921 922 /** 923 * Lookup a class stored in the local hashtable. If it is not 924 * use the global lookup function in ObjectStreamClass to build 925 * the ObjectStreamClass. This method is requested according to 926 * the behaviour detected in the JDK by Kaffe's team. 927 * 928 * @param clazz Class to lookup in the hash table or for which 929 * we must build a descriptor. 930 * @return A valid instance of ObjectStreamClass corresponding 931 * to the specified class. 932 */ lookupClass(Class clazz)933 private ObjectStreamClass lookupClass(Class clazz) 934 { 935 if (clazz == null) 936 return null; 937 938 ObjectStreamClass oclazz; 939 oclazz = classLookupTable.get(clazz); 940 if (oclazz == null) 941 return ObjectStreamClass.lookup(clazz); 942 else 943 return oclazz; 944 } 945 946 /** 947 * Reconstruct class hierarchy the same way {@link 948 * java.io.ObjectStreamClass#hierarchy} does but using lookupClass 949 * instead of ObjectStreamClass.lookup. 950 * 951 * @param clazz This is the class for which we want the hierarchy. 952 * 953 * @return An array of valid {@link java.io.ObjectStreamClass} instances which 954 * represent the class hierarchy for clazz. 955 */ hierarchy(Class clazz)956 private ObjectStreamClass[] hierarchy(Class clazz) 957 { 958 ObjectStreamClass osc = lookupClass(clazz); 959 960 return osc == null ? new ObjectStreamClass[0] : osc.hierarchy(); 961 } 962 963 /** 964 * Allows subclasses to resolve objects that are read from the 965 * stream with other objects to be returned in their place. This 966 * method is called the first time each object is encountered. 967 * 968 * This method must be enabled before it will be called in the 969 * serialization process. 970 * 971 * @exception IOException Exception from underlying 972 * <code>OutputStream</code>. 973 * 974 * @see #enableResolveObject(boolean) 975 */ resolveObject(Object obj)976 protected Object resolveObject(Object obj) throws IOException 977 { 978 return obj; 979 } 980 981 resolveProxyClass(String[] intfs)982 protected Class<?> resolveProxyClass(String[] intfs) 983 throws IOException, ClassNotFoundException 984 { 985 ClassLoader cl = currentLoader(); 986 987 Class<?>[] clss = new Class<?>[intfs.length]; 988 if(cl == null) 989 { 990 for (int i = 0; i < intfs.length; i++) 991 clss[i] = Class.forName(intfs[i]); 992 cl = ClassLoader.getSystemClassLoader(); 993 } 994 else 995 for (int i = 0; i < intfs.length; i++) 996 clss[i] = Class.forName(intfs[i], false, cl); 997 try 998 { 999 return Proxy.getProxyClass(cl, clss); 1000 } 1001 catch (IllegalArgumentException e) 1002 { 1003 throw new ClassNotFoundException(null, e); 1004 } 1005 } 1006 1007 /** 1008 * If <code>enable</code> is <code>true</code> and this object is 1009 * trusted, then <code>resolveObject (Object)</code> will be called 1010 * in subsequent calls to <code>readObject (Object)</code>. 1011 * Otherwise, <code>resolveObject (Object)</code> will not be called. 1012 * 1013 * @exception SecurityException This class is not trusted. 1014 */ enableResolveObject(boolean enable)1015 protected boolean enableResolveObject (boolean enable) 1016 throws SecurityException 1017 { 1018 if (enable) 1019 { 1020 SecurityManager sm = System.getSecurityManager(); 1021 if (sm != null) 1022 sm.checkPermission(new SerializablePermission("enableSubstitution")); 1023 } 1024 1025 boolean old_val = this.resolveEnabled; 1026 this.resolveEnabled = enable; 1027 return old_val; 1028 } 1029 1030 /** 1031 * Reads stream magic and stream version information from the 1032 * underlying stream. 1033 * 1034 * @exception IOException Exception from underlying stream. 1035 * 1036 * @exception StreamCorruptedException An invalid stream magic 1037 * number or stream version was read from the stream. 1038 */ readStreamHeader()1039 protected void readStreamHeader() 1040 throws IOException, StreamCorruptedException 1041 { 1042 if(dump) dumpElement("STREAM MAGIC "); 1043 if (this.realInputStream.readShort() != STREAM_MAGIC) 1044 throw new StreamCorruptedException("Invalid stream magic number"); 1045 1046 if(dump) dumpElementln("STREAM VERSION "); 1047 if (this.realInputStream.readShort() != STREAM_VERSION) 1048 throw new StreamCorruptedException("Invalid stream version number"); 1049 } 1050 read()1051 public int read() throws IOException 1052 { 1053 if (this.readDataFromBlock) 1054 { 1055 if (this.blockDataPosition >= this.blockDataBytes) 1056 readNextBlock(); 1057 return (this.blockData[this.blockDataPosition++] & 0xff); 1058 } 1059 else 1060 return this.realInputStream.read(); 1061 } 1062 read(byte[] data, int offset, int length)1063 public int read(byte[] data, int offset, int length) throws IOException 1064 { 1065 if (this.readDataFromBlock) 1066 { 1067 int remain = this.blockDataBytes - this.blockDataPosition; 1068 if (remain == 0) 1069 { 1070 readNextBlock(); 1071 remain = this.blockDataBytes - this.blockDataPosition; 1072 } 1073 length = Math.min(length, remain); 1074 System.arraycopy(this.blockData, this.blockDataPosition, 1075 data, offset, length); 1076 this.blockDataPosition += length; 1077 1078 return length; 1079 } 1080 else 1081 return this.realInputStream.read(data, offset, length); 1082 } 1083 available()1084 public int available() throws IOException 1085 { 1086 if (this.readDataFromBlock) 1087 { 1088 if (this.blockDataPosition >= this.blockDataBytes) 1089 readNextBlock (); 1090 1091 return this.blockDataBytes - this.blockDataPosition; 1092 } 1093 else 1094 return this.realInputStream.available(); 1095 } 1096 close()1097 public void close() throws IOException 1098 { 1099 this.realInputStream.close(); 1100 } 1101 readBoolean()1102 public boolean readBoolean() throws IOException 1103 { 1104 boolean switchmode = true; 1105 boolean oldmode = this.readDataFromBlock; 1106 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) 1107 switchmode = false; 1108 if (switchmode) 1109 oldmode = setBlockDataMode (true); 1110 boolean value = this.dataInputStream.readBoolean (); 1111 if (switchmode) 1112 setBlockDataMode (oldmode); 1113 return value; 1114 } 1115 readByte()1116 public byte readByte() throws IOException 1117 { 1118 boolean switchmode = true; 1119 boolean oldmode = this.readDataFromBlock; 1120 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) 1121 switchmode = false; 1122 if (switchmode) 1123 oldmode = setBlockDataMode(true); 1124 byte value = this.dataInputStream.readByte(); 1125 if (switchmode) 1126 setBlockDataMode(oldmode); 1127 return value; 1128 } 1129 readUnsignedByte()1130 public int readUnsignedByte() throws IOException 1131 { 1132 boolean switchmode = true; 1133 boolean oldmode = this.readDataFromBlock; 1134 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) 1135 switchmode = false; 1136 if (switchmode) 1137 oldmode = setBlockDataMode(true); 1138 int value = this.dataInputStream.readUnsignedByte(); 1139 if (switchmode) 1140 setBlockDataMode(oldmode); 1141 return value; 1142 } 1143 readShort()1144 public short readShort() throws IOException 1145 { 1146 boolean switchmode = true; 1147 boolean oldmode = this.readDataFromBlock; 1148 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) 1149 switchmode = false; 1150 if (switchmode) 1151 oldmode = setBlockDataMode(true); 1152 short value = this.dataInputStream.readShort(); 1153 if (switchmode) 1154 setBlockDataMode(oldmode); 1155 return value; 1156 } 1157 readUnsignedShort()1158 public int readUnsignedShort() throws IOException 1159 { 1160 boolean switchmode = true; 1161 boolean oldmode = this.readDataFromBlock; 1162 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) 1163 switchmode = false; 1164 if (switchmode) 1165 oldmode = setBlockDataMode(true); 1166 int value = this.dataInputStream.readUnsignedShort(); 1167 if (switchmode) 1168 setBlockDataMode(oldmode); 1169 return value; 1170 } 1171 readChar()1172 public char readChar() throws IOException 1173 { 1174 boolean switchmode = true; 1175 boolean oldmode = this.readDataFromBlock; 1176 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) 1177 switchmode = false; 1178 if (switchmode) 1179 oldmode = setBlockDataMode(true); 1180 char value = this.dataInputStream.readChar(); 1181 if (switchmode) 1182 setBlockDataMode(oldmode); 1183 return value; 1184 } 1185 readInt()1186 public int readInt() throws IOException 1187 { 1188 boolean switchmode = true; 1189 boolean oldmode = this.readDataFromBlock; 1190 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) 1191 switchmode = false; 1192 if (switchmode) 1193 oldmode = setBlockDataMode(true); 1194 int value = this.dataInputStream.readInt(); 1195 if (switchmode) 1196 setBlockDataMode(oldmode); 1197 return value; 1198 } 1199 readLong()1200 public long readLong() throws IOException 1201 { 1202 boolean switchmode = true; 1203 boolean oldmode = this.readDataFromBlock; 1204 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) 1205 switchmode = false; 1206 if (switchmode) 1207 oldmode = setBlockDataMode(true); 1208 long value = this.dataInputStream.readLong(); 1209 if (switchmode) 1210 setBlockDataMode(oldmode); 1211 return value; 1212 } 1213 readFloat()1214 public float readFloat() throws IOException 1215 { 1216 boolean switchmode = true; 1217 boolean oldmode = this.readDataFromBlock; 1218 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) 1219 switchmode = false; 1220 if (switchmode) 1221 oldmode = setBlockDataMode(true); 1222 float value = this.dataInputStream.readFloat(); 1223 if (switchmode) 1224 setBlockDataMode(oldmode); 1225 return value; 1226 } 1227 readDouble()1228 public double readDouble() throws IOException 1229 { 1230 boolean switchmode = true; 1231 boolean oldmode = this.readDataFromBlock; 1232 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) 1233 switchmode = false; 1234 if (switchmode) 1235 oldmode = setBlockDataMode(true); 1236 double value = this.dataInputStream.readDouble(); 1237 if (switchmode) 1238 setBlockDataMode(oldmode); 1239 return value; 1240 } 1241 readFully(byte data[])1242 public void readFully(byte data[]) throws IOException 1243 { 1244 this.dataInputStream.readFully(data); 1245 } 1246 readFully(byte data[], int offset, int size)1247 public void readFully(byte data[], int offset, int size) 1248 throws IOException 1249 { 1250 this.dataInputStream.readFully(data, offset, size); 1251 } 1252 skipBytes(int len)1253 public int skipBytes(int len) throws IOException 1254 { 1255 return this.dataInputStream.skipBytes(len); 1256 } 1257 1258 /** 1259 * @deprecated 1260 * @see java.io.DataInputStream#readLine () 1261 */ readLine()1262 public String readLine() throws IOException 1263 { 1264 return this.dataInputStream.readLine(); 1265 } 1266 readUTF()1267 public String readUTF() throws IOException 1268 { 1269 return this.dataInputStream.readUTF(); 1270 } 1271 1272 /** 1273 * This class allows a class to specify exactly which fields should 1274 * be read, and what values should be read for these fields. 1275 * 1276 * XXX: finish up comments 1277 */ 1278 public abstract static class GetField 1279 { getObjectStreamClass()1280 public abstract ObjectStreamClass getObjectStreamClass(); 1281 defaulted(String name)1282 public abstract boolean defaulted(String name) 1283 throws IOException, IllegalArgumentException; 1284 get(String name, boolean defvalue)1285 public abstract boolean get(String name, boolean defvalue) 1286 throws IOException, IllegalArgumentException; 1287 get(String name, char defvalue)1288 public abstract char get(String name, char defvalue) 1289 throws IOException, IllegalArgumentException; 1290 get(String name, byte defvalue)1291 public abstract byte get(String name, byte defvalue) 1292 throws IOException, IllegalArgumentException; 1293 get(String name, short defvalue)1294 public abstract short get(String name, short defvalue) 1295 throws IOException, IllegalArgumentException; 1296 get(String name, int defvalue)1297 public abstract int get(String name, int defvalue) 1298 throws IOException, IllegalArgumentException; 1299 get(String name, long defvalue)1300 public abstract long get(String name, long defvalue) 1301 throws IOException, IllegalArgumentException; 1302 get(String name, float defvalue)1303 public abstract float get(String name, float defvalue) 1304 throws IOException, IllegalArgumentException; 1305 get(String name, double defvalue)1306 public abstract double get(String name, double defvalue) 1307 throws IOException, IllegalArgumentException; 1308 get(String name, Object defvalue)1309 public abstract Object get(String name, Object defvalue) 1310 throws IOException, IllegalArgumentException; 1311 } 1312 1313 /** 1314 * This method should be called by a method called 'readObject' in the 1315 * deserializing class (if present). It cannot (and should not)be called 1316 * outside of it. Its goal is to read all fields in the real input stream 1317 * and keep them accessible through the {@link GetField} class. Calling 1318 * this method will not alter the deserializing object. 1319 * 1320 * @return A valid freshly created 'GetField' instance to get access to 1321 * the deserialized stream. 1322 * @throws IOException An input/output exception occured. 1323 * @throws ClassNotFoundException 1324 * @throws NotActiveException 1325 */ readFields()1326 public GetField readFields() 1327 throws IOException, ClassNotFoundException, NotActiveException 1328 { 1329 if (this.currentObject == null || this.currentObjectStreamClass == null) 1330 throw new NotActiveException("readFields called by non-active class and/or object"); 1331 1332 if (prereadFields != null) 1333 return prereadFields; 1334 1335 if (fieldsAlreadyRead) 1336 throw new NotActiveException("readFields called but fields already read from" 1337 + " stream (by defaultReadObject or readFields)"); 1338 1339 final ObjectStreamClass clazz = this.currentObjectStreamClass; 1340 final byte[] prim_field_data = new byte[clazz.primFieldSize]; 1341 final Object[] objs = new Object[clazz.objectFieldCount]; 1342 1343 // Apparently Block data is not used with GetField as per 1344 // empirical evidence against JDK 1.2. Also see Mauve test 1345 // java.io.ObjectInputOutput.Test.GetPutField. 1346 boolean oldmode = setBlockDataMode(false); 1347 readFully(prim_field_data); 1348 for (int i = 0; i < objs.length; ++ i) 1349 objs[i] = readObject(); 1350 setBlockDataMode(oldmode); 1351 1352 prereadFields = new GetField() 1353 { 1354 public ObjectStreamClass getObjectStreamClass() 1355 { 1356 return clazz; 1357 } 1358 1359 public boolean defaulted(String name) 1360 throws IOException, IllegalArgumentException 1361 { 1362 ObjectStreamField f = clazz.getField(name); 1363 1364 /* First if we have a serialized field use the descriptor */ 1365 if (f != null) 1366 { 1367 /* It is in serialPersistentFields but setClass tells us 1368 * it should not be set. This value is defaulted. 1369 */ 1370 if (f.isPersistent() && !f.isToSet()) 1371 return true; 1372 1373 return false; 1374 } 1375 1376 /* This is not a serialized field. There should be 1377 * a default value only if the field really exists. 1378 */ 1379 try 1380 { 1381 return (clazz.forClass().getDeclaredField (name) != null); 1382 } 1383 catch (NoSuchFieldException e) 1384 { 1385 throw new IllegalArgumentException(e); 1386 } 1387 } 1388 1389 public boolean get(String name, boolean defvalue) 1390 throws IOException, IllegalArgumentException 1391 { 1392 ObjectStreamField field = getField(name, Boolean.TYPE); 1393 1394 if (field == null) 1395 return defvalue; 1396 1397 return prim_field_data[field.getOffset()] == 0 ? false : true; 1398 } 1399 1400 public char get(String name, char defvalue) 1401 throws IOException, IllegalArgumentException 1402 { 1403 ObjectStreamField field = getField(name, Character.TYPE); 1404 1405 if (field == null) 1406 return defvalue; 1407 1408 int off = field.getOffset(); 1409 1410 return (char)(((prim_field_data[off++] & 0xFF) << 8) 1411 | (prim_field_data[off] & 0xFF)); 1412 } 1413 1414 public byte get(String name, byte defvalue) 1415 throws IOException, IllegalArgumentException 1416 { 1417 ObjectStreamField field = getField(name, Byte.TYPE); 1418 1419 if (field == null) 1420 return defvalue; 1421 1422 return prim_field_data[field.getOffset()]; 1423 } 1424 1425 public short get(String name, short defvalue) 1426 throws IOException, IllegalArgumentException 1427 { 1428 ObjectStreamField field = getField(name, Short.TYPE); 1429 1430 if (field == null) 1431 return defvalue; 1432 1433 int off = field.getOffset(); 1434 1435 return (short)(((prim_field_data[off++] & 0xFF) << 8) 1436 | (prim_field_data[off] & 0xFF)); 1437 } 1438 1439 public int get(String name, int defvalue) 1440 throws IOException, IllegalArgumentException 1441 { 1442 ObjectStreamField field = getField(name, Integer.TYPE); 1443 1444 if (field == null) 1445 return defvalue; 1446 1447 int off = field.getOffset(); 1448 1449 return ((prim_field_data[off++] & 0xFF) << 24) 1450 | ((prim_field_data[off++] & 0xFF) << 16) 1451 | ((prim_field_data[off++] & 0xFF) << 8) 1452 | (prim_field_data[off] & 0xFF); 1453 } 1454 1455 public long get(String name, long defvalue) 1456 throws IOException, IllegalArgumentException 1457 { 1458 ObjectStreamField field = getField(name, Long.TYPE); 1459 1460 if (field == null) 1461 return defvalue; 1462 1463 int off = field.getOffset(); 1464 1465 return (long)(((prim_field_data[off++] & 0xFFL) << 56) 1466 | ((prim_field_data[off++] & 0xFFL) << 48) 1467 | ((prim_field_data[off++] & 0xFFL) << 40) 1468 | ((prim_field_data[off++] & 0xFFL) << 32) 1469 | ((prim_field_data[off++] & 0xFF) << 24) 1470 | ((prim_field_data[off++] & 0xFF) << 16) 1471 | ((prim_field_data[off++] & 0xFF) << 8) 1472 | (prim_field_data[off] & 0xFF)); 1473 } 1474 1475 public float get(String name, float defvalue) 1476 throws IOException, IllegalArgumentException 1477 { 1478 ObjectStreamField field = getField(name, Float.TYPE); 1479 1480 if (field == null) 1481 return defvalue; 1482 1483 int off = field.getOffset(); 1484 1485 return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24) 1486 | ((prim_field_data[off++] & 0xFF) << 16) 1487 | ((prim_field_data[off++] & 0xFF) << 8) 1488 | (prim_field_data[off] & 0xFF)); 1489 } 1490 1491 public double get(String name, double defvalue) 1492 throws IOException, IllegalArgumentException 1493 { 1494 ObjectStreamField field = getField(name, Double.TYPE); 1495 1496 if (field == null) 1497 return defvalue; 1498 1499 int off = field.getOffset(); 1500 1501 return Double.longBitsToDouble 1502 ( (long) (((prim_field_data[off++] & 0xFFL) << 56) 1503 | ((prim_field_data[off++] & 0xFFL) << 48) 1504 | ((prim_field_data[off++] & 0xFFL) << 40) 1505 | ((prim_field_data[off++] & 0xFFL) << 32) 1506 | ((prim_field_data[off++] & 0xFF) << 24) 1507 | ((prim_field_data[off++] & 0xFF) << 16) 1508 | ((prim_field_data[off++] & 0xFF) << 8) 1509 | (prim_field_data[off] & 0xFF))); 1510 } 1511 1512 public Object get(String name, Object defvalue) 1513 throws IOException, IllegalArgumentException 1514 { 1515 ObjectStreamField field = 1516 getField(name, defvalue == null ? null : defvalue.getClass ()); 1517 1518 if (field == null) 1519 return defvalue; 1520 1521 return objs[field.getOffset()]; 1522 } 1523 1524 private ObjectStreamField getField(String name, Class type) 1525 throws IllegalArgumentException 1526 { 1527 ObjectStreamField field = clazz.getField(name); 1528 boolean illegal = false; 1529 1530 // XXX This code is horrible and needs to be rewritten! 1531 try 1532 { 1533 try 1534 { 1535 Class field_type = field.getType(); 1536 1537 if (type == field_type || 1538 (type == null && !field_type.isPrimitive())) 1539 { 1540 /* See defaulted */ 1541 return field; 1542 } 1543 1544 illegal = true; 1545 throw new IllegalArgumentException 1546 ("Field requested is of type " 1547 + field_type.getName() 1548 + ", but requested type was " 1549 + (type == null ? "Object" : type.getName())); 1550 } 1551 catch (NullPointerException _) 1552 { 1553 /* Here we catch NullPointerException, because it may 1554 only come from the call 'field.getType()'. If field 1555 is null, we have to return null and classpath ethic 1556 say we must try to avoid 'if (xxx == null)'. 1557 */ 1558 } 1559 catch (IllegalArgumentException e) 1560 { 1561 throw e; 1562 } 1563 1564 return null; 1565 } 1566 finally 1567 { 1568 /* If this is an unassigned field we should return 1569 * the default value. 1570 */ 1571 if (!illegal && field != null && !field.isToSet() && field.isPersistent()) 1572 return null; 1573 1574 /* We do not want to modify transient fields. They should 1575 * be left to 0. 1576 */ 1577 try 1578 { 1579 Field f = clazz.forClass().getDeclaredField(name); 1580 if (Modifier.isTransient(f.getModifiers())) 1581 throw new IllegalArgumentException 1582 ("no such field (non transient) " + name); 1583 if (field == null && f.getType() != type) 1584 throw new IllegalArgumentException 1585 ("Invalid requested type for field " + name); 1586 } 1587 catch (NoSuchFieldException e) 1588 { 1589 if (field == null) 1590 throw new IllegalArgumentException(e); 1591 } 1592 1593 } 1594 } 1595 }; 1596 1597 fieldsAlreadyRead = true; 1598 return prereadFields; 1599 } 1600 1601 /** 1602 * Protected constructor that allows subclasses to override 1603 * deserialization. This constructor should be called by subclasses 1604 * that wish to override <code>readObject (Object)</code>. This 1605 * method does a security check <i>NOTE: currently not 1606 * implemented</i>, then sets a flag that informs 1607 * <code>readObject (Object)</code> to call the subclasses 1608 * <code>readObjectOverride (Object)</code> method. 1609 * 1610 * @see #readObjectOverride() 1611 */ ObjectInputStream()1612 protected ObjectInputStream() 1613 throws IOException, SecurityException 1614 { 1615 SecurityManager sec_man = System.getSecurityManager(); 1616 if (sec_man != null) 1617 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 1618 this.useSubclassMethod = true; 1619 } 1620 1621 /** 1622 * This method allows subclasses to override the default 1623 * de serialization mechanism provided by 1624 * <code>ObjectInputStream</code>. To make this method be used for 1625 * writing objects, subclasses must invoke the 0-argument 1626 * constructor on this class from their constructor. 1627 * 1628 * @see #ObjectInputStream() 1629 */ readObjectOverride()1630 protected Object readObjectOverride() 1631 throws ClassNotFoundException, IOException, OptionalDataException 1632 { 1633 throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride"); 1634 } 1635 1636 /** 1637 * Assigns the next available handle to <code>obj</code>. 1638 * 1639 * @param obj The object for which we want a new handle. 1640 * @param shared True if the handle should be shared 1641 * with later calls. 1642 * @return A valid handle for the specified object. 1643 */ assignNewHandle(Object obj, boolean shared)1644 private int assignNewHandle(Object obj, boolean shared) 1645 { 1646 int handle = this.nextOID; 1647 this.nextOID = handle + 1; 1648 rememberHandle(obj,shared,handle); 1649 return handle; 1650 } 1651 1652 /** 1653 * Remember the object associated with the given handle. 1654 * 1655 * @param obj an object 1656 * @param shared true if the reference should be shared 1657 * with later calls. 1658 * @param handle a handle, must be >= baseWireHandle 1659 * 1660 * @see #lookupHandle 1661 */ rememberHandle(Object obj, boolean shared, int handle)1662 private void rememberHandle(Object obj, boolean shared, 1663 int handle) 1664 { 1665 handles.put(handle, new Pair<Boolean,Object>(shared, obj)); 1666 } 1667 1668 /** 1669 * Look up the object associated with a given handle. 1670 * 1671 * @param handle a handle, must be >= baseWireHandle 1672 * @return the object remembered for handle or null if none. 1673 * @throws StreamCorruptedException if the handle is invalid. 1674 * @throws InvalidObjectException if the reference is not shared. 1675 * @see #rememberHandle 1676 */ lookupHandle(int handle)1677 private Object lookupHandle(int handle) 1678 throws ObjectStreamException 1679 { 1680 Pair<Boolean,Object> result = handles.get(handle); 1681 if (result == null) 1682 throw new StreamCorruptedException("The handle, " + 1683 Integer.toHexString(handle) + 1684 ", is invalid."); 1685 if (!result.getLeft()) 1686 throw new InvalidObjectException("The handle, " + 1687 Integer.toHexString(handle) + 1688 ", is not shared."); 1689 return result.getRight(); 1690 } 1691 processResolution(ObjectStreamClass osc, Object obj, int handle, boolean shared)1692 private Object processResolution(ObjectStreamClass osc, Object obj, int handle, 1693 boolean shared) 1694 throws IOException 1695 { 1696 if (osc != null && obj instanceof Serializable) 1697 { 1698 try 1699 { 1700 Method m = osc.readResolveMethod; 1701 if(m != null) 1702 { 1703 obj = m.invoke(obj, new Object[] {}); 1704 } 1705 } 1706 catch (IllegalAccessException ignore) 1707 { 1708 } 1709 catch (InvocationTargetException exception) 1710 { 1711 Throwable cause = exception.getCause(); 1712 if (cause instanceof ObjectStreamException) 1713 throw (ObjectStreamException) cause; 1714 else if (cause instanceof RuntimeException) 1715 throw (RuntimeException) cause; 1716 else if (cause instanceof Error) 1717 throw (Error) cause; 1718 } 1719 } 1720 1721 if (this.resolveEnabled) 1722 obj = resolveObject(obj); 1723 1724 rememberHandle(obj, shared, handle); 1725 if (!shared) 1726 { 1727 if (obj instanceof byte[]) 1728 return ((byte[]) obj).clone(); 1729 if (obj instanceof short[]) 1730 return ((short[]) obj).clone(); 1731 if (obj instanceof int[]) 1732 return ((int[]) obj).clone(); 1733 if (obj instanceof long[]) 1734 return ((long[]) obj).clone(); 1735 if (obj instanceof char[]) 1736 return ((char[]) obj).clone(); 1737 if (obj instanceof boolean[]) 1738 return ((boolean[]) obj).clone(); 1739 if (obj instanceof float[]) 1740 return ((float[]) obj).clone(); 1741 if (obj instanceof double[]) 1742 return ((double[]) obj).clone(); 1743 if (obj instanceof Object[]) 1744 return ((Object[]) obj).clone(); 1745 } 1746 return obj; 1747 } 1748 clearHandles()1749 private void clearHandles() 1750 { 1751 handles.clear(); 1752 this.nextOID = baseWireHandle; 1753 } 1754 readNextBlock()1755 private void readNextBlock() throws IOException 1756 { 1757 byte marker = this.realInputStream.readByte(); 1758 while (marker == TC_RESET) 1759 { 1760 if(dump) dumpElementln("RESET"); 1761 clearHandles(); 1762 marker = this.realInputStream.readByte(); 1763 } 1764 readNextBlock(marker); 1765 } 1766 readNextBlock(byte marker)1767 private void readNextBlock(byte marker) throws IOException 1768 { 1769 if (marker == TC_BLOCKDATA) 1770 { 1771 if(dump) dumpElement("BLOCK DATA SIZE="); 1772 this.blockDataBytes = this.realInputStream.readUnsignedByte(); 1773 if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); 1774 } 1775 else if (marker == TC_BLOCKDATALONG) 1776 { 1777 if(dump) dumpElement("BLOCK DATA LONG SIZE="); 1778 this.blockDataBytes = this.realInputStream.readInt(); 1779 if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); 1780 } 1781 else 1782 { 1783 throw new EOFException("Attempt to read primitive data, but no data block is active."); 1784 } 1785 1786 if (this.blockData.length < this.blockDataBytes) 1787 this.blockData = new byte[this.blockDataBytes]; 1788 1789 this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); 1790 this.blockDataPosition = 0; 1791 } 1792 readArrayElements(Object array, Class clazz)1793 private void readArrayElements (Object array, Class clazz) 1794 throws ClassNotFoundException, IOException 1795 { 1796 if (clazz.isPrimitive()) 1797 { 1798 if (clazz == Boolean.TYPE) 1799 { 1800 boolean[] cast_array = (boolean[])array; 1801 for (int i=0; i < cast_array.length; i++) 1802 cast_array[i] = this.realInputStream.readBoolean(); 1803 return; 1804 } 1805 if (clazz == Byte.TYPE) 1806 { 1807 byte[] cast_array = (byte[])array; 1808 for (int i=0; i < cast_array.length; i++) 1809 cast_array[i] = this.realInputStream.readByte(); 1810 return; 1811 } 1812 if (clazz == Character.TYPE) 1813 { 1814 char[] cast_array = (char[])array; 1815 for (int i=0; i < cast_array.length; i++) 1816 cast_array[i] = this.realInputStream.readChar(); 1817 return; 1818 } 1819 if (clazz == Double.TYPE) 1820 { 1821 double[] cast_array = (double[])array; 1822 for (int i=0; i < cast_array.length; i++) 1823 cast_array[i] = this.realInputStream.readDouble(); 1824 return; 1825 } 1826 if (clazz == Float.TYPE) 1827 { 1828 float[] cast_array = (float[])array; 1829 for (int i=0; i < cast_array.length; i++) 1830 cast_array[i] = this.realInputStream.readFloat(); 1831 return; 1832 } 1833 if (clazz == Integer.TYPE) 1834 { 1835 int[] cast_array = (int[])array; 1836 for (int i=0; i < cast_array.length; i++) 1837 cast_array[i] = this.realInputStream.readInt(); 1838 return; 1839 } 1840 if (clazz == Long.TYPE) 1841 { 1842 long[] cast_array = (long[])array; 1843 for (int i=0; i < cast_array.length; i++) 1844 cast_array[i] = this.realInputStream.readLong(); 1845 return; 1846 } 1847 if (clazz == Short.TYPE) 1848 { 1849 short[] cast_array = (short[])array; 1850 for (int i=0; i < cast_array.length; i++) 1851 cast_array[i] = this.realInputStream.readShort(); 1852 return; 1853 } 1854 } 1855 else 1856 { 1857 Object[] cast_array = (Object[])array; 1858 for (int i=0; i < cast_array.length; i++) 1859 cast_array[i] = readObject(); 1860 } 1861 } 1862 readFields(Object obj, ObjectStreamClass stream_osc)1863 private void readFields (Object obj, ObjectStreamClass stream_osc) 1864 throws ClassNotFoundException, IOException 1865 { 1866 ObjectStreamField[] fields = stream_osc.fieldMapping; 1867 1868 for (int i = 0; i < fields.length; i += 2) 1869 { 1870 ObjectStreamField stream_field = fields[i]; 1871 ObjectStreamField real_field = fields[i + 1]; 1872 boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet()); 1873 boolean set_value = (real_field != null && real_field.isToSet()); 1874 String field_name; 1875 char type; 1876 1877 if (stream_field != null) 1878 { 1879 field_name = stream_field.getName(); 1880 type = stream_field.getTypeCode(); 1881 } 1882 else 1883 { 1884 field_name = real_field.getName(); 1885 type = real_field.getTypeCode(); 1886 } 1887 1888 switch(type) 1889 { 1890 case 'Z': 1891 { 1892 boolean value = 1893 read_value ? this.realInputStream.readBoolean() : false; 1894 if (dump && read_value && set_value) 1895 dumpElementln(" " + field_name + ": " + value); 1896 if (set_value) 1897 real_field.setBooleanField(obj, value); 1898 break; 1899 } 1900 case 'B': 1901 { 1902 byte value = 1903 read_value ? this.realInputStream.readByte() : 0; 1904 if (dump && read_value && set_value) 1905 dumpElementln(" " + field_name + ": " + value); 1906 if (set_value) 1907 real_field.setByteField(obj, value); 1908 break; 1909 } 1910 case 'C': 1911 { 1912 char value = 1913 read_value ? this.realInputStream.readChar(): 0; 1914 if (dump && read_value && set_value) 1915 dumpElementln(" " + field_name + ": " + value); 1916 if (set_value) 1917 real_field.setCharField(obj, value); 1918 break; 1919 } 1920 case 'D': 1921 { 1922 double value = 1923 read_value ? this.realInputStream.readDouble() : 0; 1924 if (dump && read_value && set_value) 1925 dumpElementln(" " + field_name + ": " + value); 1926 if (set_value) 1927 real_field.setDoubleField(obj, value); 1928 break; 1929 } 1930 case 'F': 1931 { 1932 float value = 1933 read_value ? this.realInputStream.readFloat() : 0; 1934 if (dump && read_value && set_value) 1935 dumpElementln(" " + field_name + ": " + value); 1936 if (set_value) 1937 real_field.setFloatField(obj, value); 1938 break; 1939 } 1940 case 'I': 1941 { 1942 int value = 1943 read_value ? this.realInputStream.readInt() : 0; 1944 if (dump && read_value && set_value) 1945 dumpElementln(" " + field_name + ": " + value); 1946 if (set_value) 1947 real_field.setIntField(obj, value); 1948 break; 1949 } 1950 case 'J': 1951 { 1952 long value = 1953 read_value ? this.realInputStream.readLong() : 0; 1954 if (dump && read_value && set_value) 1955 dumpElementln(" " + field_name + ": " + value); 1956 if (set_value) 1957 real_field.setLongField(obj, value); 1958 break; 1959 } 1960 case 'S': 1961 { 1962 short value = 1963 read_value ? this.realInputStream.readShort() : 0; 1964 if (dump && read_value && set_value) 1965 dumpElementln(" " + field_name + ": " + value); 1966 if (set_value) 1967 real_field.setShortField(obj, value); 1968 break; 1969 } 1970 case 'L': 1971 case '[': 1972 { 1973 Object value = 1974 read_value ? readObject() : null; 1975 if (set_value) 1976 real_field.setObjectField(obj, value); 1977 break; 1978 } 1979 default: 1980 throw new InternalError("Invalid type code: " + type); 1981 } 1982 } 1983 } 1984 1985 // Toggles writing primitive data to block-data buffer. setBlockDataMode(boolean on)1986 private boolean setBlockDataMode (boolean on) 1987 { 1988 boolean oldmode = this.readDataFromBlock; 1989 this.readDataFromBlock = on; 1990 1991 if (on) 1992 this.dataInputStream = this.blockDataInput; 1993 else 1994 this.dataInputStream = this.realInputStream; 1995 return oldmode; 1996 } 1997 1998 // returns a new instance of REAL_CLASS that has been constructed 1999 // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) newObject(Class real_class, Constructor constructor)2000 private Object newObject (Class real_class, Constructor constructor) 2001 throws ClassNotFoundException, IOException 2002 { 2003 if (constructor == null) 2004 throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); 2005 try 2006 { 2007 return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor); 2008 } 2009 catch (InstantiationException e) 2010 { 2011 throw (ClassNotFoundException) new ClassNotFoundException 2012 ("Instance of " + real_class + " could not be created").initCause(e); 2013 } 2014 } 2015 2016 // runs all registered ObjectInputValidations in prioritized order 2017 // on OBJ invokeValidators()2018 private void invokeValidators() throws InvalidObjectException 2019 { 2020 try 2021 { 2022 Iterator<ValidatorAndPriority> it = currentObjectValidators.iterator(); 2023 while(it.hasNext()) 2024 { 2025 ValidatorAndPriority vap = it.next(); 2026 ObjectInputValidation validator = vap.validator; 2027 validator.validateObject(); 2028 } 2029 } 2030 finally 2031 { 2032 currentObjectValidators = null; 2033 } 2034 } 2035 callReadMethod(Method readObject, Class klass, Object obj)2036 private void callReadMethod (Method readObject, Class klass, Object obj) 2037 throws ClassNotFoundException, IOException 2038 { 2039 try 2040 { 2041 readObject.invoke(obj, new Object[] { this }); 2042 } 2043 catch (InvocationTargetException x) 2044 { 2045 /* Rethrow if possible. */ 2046 Throwable exception = x.getTargetException(); 2047 if (exception instanceof RuntimeException) 2048 throw (RuntimeException) exception; 2049 if (exception instanceof IOException) 2050 throw (IOException) exception; 2051 if (exception instanceof ClassNotFoundException) 2052 throw (ClassNotFoundException) exception; 2053 2054 throw (IOException) new IOException( 2055 "Exception thrown from readObject() on " + klass).initCause(x); 2056 } 2057 catch (Exception x) 2058 { 2059 throw (IOException) new IOException( 2060 "Failure invoking readObject() on " + klass).initCause(x); 2061 } 2062 2063 // Invalidate fields which has been read through readFields. 2064 prereadFields = null; 2065 } 2066 2067 private static final int BUFFER_SIZE = 1024; 2068 2069 private DataInputStream realInputStream; 2070 private DataInputStream dataInputStream; 2071 private DataInputStream blockDataInput; 2072 private int blockDataPosition; 2073 private int blockDataBytes; 2074 private byte[] blockData; 2075 private boolean useSubclassMethod; 2076 private int nextOID; 2077 private boolean resolveEnabled; 2078 private Map<Integer,Pair<Boolean,Object>> handles; 2079 private Object currentObject; 2080 private ObjectStreamClass currentObjectStreamClass; 2081 private TreeSet<ValidatorAndPriority> currentObjectValidators; 2082 private boolean readDataFromBlock; 2083 private boolean fieldsAlreadyRead; 2084 private Hashtable<Class,ObjectStreamClass> classLookupTable; 2085 private GetField prereadFields; 2086 2087 private static boolean dump; 2088 2089 // The nesting depth for debugging output 2090 private int depth = 0; 2091 2092 private static final boolean DEBUG = false; 2093 dumpElement(String msg)2094 private void dumpElement (String msg) 2095 { 2096 System.out.print(msg); 2097 } 2098 dumpElementln(String msg)2099 private void dumpElementln (String msg) 2100 { 2101 System.out.println(msg); 2102 for (int i = 0; i < depth; i++) 2103 System.out.print (" "); 2104 System.out.print (Thread.currentThread() + ": "); 2105 } 2106 dumpElementln(String msg, Object obj)2107 private void dumpElementln (String msg, Object obj) 2108 { 2109 try 2110 { 2111 System.out.print(msg); 2112 if (java.lang.reflect.Proxy.isProxyClass(obj.getClass())) 2113 System.out.println(obj.getClass()); 2114 else 2115 System.out.println(obj); 2116 } 2117 catch (Exception _) 2118 { 2119 } 2120 for (int i = 0; i < depth; i++) 2121 System.out.print (" "); 2122 System.out.print (Thread.currentThread() + ": "); 2123 } 2124 2125 // used to keep a prioritized list of object validators 2126 private static final class ValidatorAndPriority implements Comparable 2127 { 2128 int priority; 2129 ObjectInputValidation validator; 2130 ValidatorAndPriority(ObjectInputValidation validator, int priority)2131 ValidatorAndPriority (ObjectInputValidation validator, int priority) 2132 { 2133 this.priority = priority; 2134 this.validator = validator; 2135 } 2136 compareTo(Object o)2137 public int compareTo (Object o) 2138 { 2139 ValidatorAndPriority vap = (ValidatorAndPriority)o; 2140 return this.priority - vap.priority; 2141 } 2142 } 2143 } 2144