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

Mon, 20 Apr 2009 00:12:19 -0700

author
tbell
date
Mon, 20 Apr 2009 00:12:19 -0700
changeset 68
4906dae0c5fa
parent 1
55540e827aef
child 98
a12ea7c7b497
permissions
-rw-r--r--

6372405: Server thread hangs when fragments don't complete because of connection abort
5104239: Java: thread deadlock
6191561: JCK15: api/org_omg/PortableInterceptor/ClientRequestInfo/index.html#RIMethods sometime hang
6486322: org.omg.CORBA.ORB.init() thread safety issue
6420980: Security issue with the com.sun.corba.se.impl.orbutil.ORBUtility class
6465377: NullPointerException for RMI ORB in 1.5.0_08
6553303: Corba application fails w/ org.omg.CORBA.COMM_FAILURE: vmcid: SUN minor code: 203 completed: No
6438259: Wrong repository ID generated by IDLJ
Reviewed-by: darcy

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

mercurial