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

Wed, 08 Dec 2010 10:43:16 -0800

author
miroslawzn
date
Wed, 08 Dec 2010 10:43:16 -0800
changeset 235
5d9708346d50
parent 158
91006f157c46
child 240
f90b3e014e83
permissions
-rw-r--r--

6877056: SVUID calculated for java.lang.Enum is not 0L
Reviewed-by: raginip

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

mercurial