src/share/classes/com/sun/corba/se/impl/io/ObjectStreamClass.java

Tue, 06 Nov 2012 15:50:14 +0000

author
coffeys
date
Tue, 06 Nov 2012 15:50:14 +0000
changeset 446
f4f39d873b9a
parent 444
80882eae6279
child 615
8b0b643ffd42
permissions
-rw-r--r--

7201066: Change modifiers on unused fields
Reviewed-by: alanb, skoivu

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

mercurial