Tue, 06 Nov 2012 15:50:14 +0000
7201066: Change modifiers on unused fields
Reviewed-by: alanb, skoivu
duke@1 | 1 | /* |
coffeys@446 | 2 | * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. |
duke@1 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
duke@1 | 4 | * |
duke@1 | 5 | * This code is free software; you can redistribute it and/or modify it |
duke@1 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@158 | 7 | * published by the Free Software Foundation. Oracle designates this |
duke@1 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@158 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
duke@1 | 10 | * |
duke@1 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
duke@1 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
duke@1 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
duke@1 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
duke@1 | 15 | * accompanied this code). |
duke@1 | 16 | * |
duke@1 | 17 | * You should have received a copy of the GNU General Public License version |
duke@1 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
duke@1 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
duke@1 | 20 | * |
ohair@158 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@158 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@158 | 23 | * questions. |
duke@1 | 24 | */ |
duke@1 | 25 | /* |
duke@1 | 26 | * Licensed Materials - Property of IBM |
duke@1 | 27 | * RMI-IIOP v1.0 |
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.orbutil; |
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 org.omg.CORBA.ValueMember; |
duke@1 | 62 | |
duke@1 | 63 | import com.sun.corba.se.impl.io.ValueUtility; |
duke@1 | 64 | import com.sun.corba.se.impl.io.ObjectStreamClass; |
duke@1 | 65 | |
duke@1 | 66 | /** |
duke@1 | 67 | * This is duplicated here to preserve the JDK 1.3.1FCS behavior |
duke@1 | 68 | * of calculating the OMG hash code incorrectly when serialPersistentFields |
duke@1 | 69 | * is used, but some of the fields no longer exist in the class itself. |
duke@1 | 70 | * |
duke@1 | 71 | * We have to duplicate it since we aren't allowed to modify the |
duke@1 | 72 | * com.sun.corba.se.impl.io version further, and can't make it |
duke@1 | 73 | * public outside of its package for security reasons. |
duke@1 | 74 | */ |
duke@1 | 75 | /** |
duke@1 | 76 | * A ObjectStreamClass_1_3_1 describes a class that can be serialized to a stream |
duke@1 | 77 | * or a class that was serialized to a stream. It contains the name |
duke@1 | 78 | * and the serialVersionUID of the class. |
duke@1 | 79 | * <br> |
duke@1 | 80 | * The ObjectStreamClass_1_3_1 for a specific class loaded in this Java VM can |
duke@1 | 81 | * be found using the lookup method. |
duke@1 | 82 | * |
duke@1 | 83 | * @author Roger Riggs |
duke@1 | 84 | * @since JDK1.1 |
duke@1 | 85 | */ |
duke@1 | 86 | public class ObjectStreamClass_1_3_1 implements java.io.Serializable { |
duke@1 | 87 | |
duke@1 | 88 | public static final long kDefaultUID = -1; |
duke@1 | 89 | |
duke@1 | 90 | private static Object noArgsList[] = {}; |
duke@1 | 91 | private static Class noTypesList[] = {}; |
duke@1 | 92 | |
duke@1 | 93 | private static Hashtable translatedFields; |
duke@1 | 94 | |
duke@1 | 95 | /** Find the descriptor for a class that can be serialized. Null |
duke@1 | 96 | * is returned if the specified class does not implement |
duke@1 | 97 | * java.io.Serializable or java.io.Externalizable. |
duke@1 | 98 | */ |
duke@1 | 99 | static final ObjectStreamClass_1_3_1 lookup(Class cl) |
duke@1 | 100 | { |
duke@1 | 101 | ObjectStreamClass_1_3_1 desc = lookupInternal(cl); |
duke@1 | 102 | if (desc.isSerializable() || desc.isExternalizable()) |
duke@1 | 103 | return desc; |
duke@1 | 104 | return null; |
duke@1 | 105 | } |
duke@1 | 106 | |
duke@1 | 107 | /* |
duke@1 | 108 | * Find the class descriptor for the specified class. |
duke@1 | 109 | * Package access only so it can be called from ObjectIn/OutStream. |
duke@1 | 110 | */ |
duke@1 | 111 | static ObjectStreamClass_1_3_1 lookupInternal(Class cl) |
duke@1 | 112 | { |
duke@1 | 113 | /* Synchronize on the hashtable so no two threads will do |
duke@1 | 114 | * this at the same time. |
duke@1 | 115 | */ |
duke@1 | 116 | ObjectStreamClass_1_3_1 desc = null; |
duke@1 | 117 | synchronized (descriptorFor) { |
duke@1 | 118 | /* Find the matching descriptor if it already known */ |
duke@1 | 119 | desc = findDescriptorFor(cl); |
duke@1 | 120 | if (desc != null) { |
duke@1 | 121 | return desc; |
duke@1 | 122 | } |
duke@1 | 123 | |
duke@1 | 124 | /* Check if it's serializable */ |
duke@1 | 125 | boolean serializable = classSerializable.isAssignableFrom(cl); |
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_1_3_1 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_1_3_1(cl, superdesc, |
duke@1 | 154 | serializable, externalizable); |
duke@1 | 155 | } |
duke@1 | 156 | desc.init(); |
duke@1 | 157 | return desc; |
duke@1 | 158 | } |
duke@1 | 159 | |
duke@1 | 160 | /** |
duke@1 | 161 | * The name of the class described by this descriptor. |
duke@1 | 162 | */ |
duke@1 | 163 | public final String getName() { |
duke@1 | 164 | return name; |
duke@1 | 165 | } |
duke@1 | 166 | |
duke@1 | 167 | /** |
duke@1 | 168 | * Return the serialVersionUID for this class. |
duke@1 | 169 | * The serialVersionUID defines a set of classes all with the same name |
duke@1 | 170 | * that have evolved from a common root class and agree to be serialized |
duke@1 | 171 | * and deserialized using a common format. |
duke@1 | 172 | */ |
duke@1 | 173 | public static final long getSerialVersionUID( java.lang.Class clazz) { |
duke@1 | 174 | ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz ); |
duke@1 | 175 | if( theosc != null ) |
duke@1 | 176 | { |
duke@1 | 177 | return theosc.getSerialVersionUID( ); |
duke@1 | 178 | } |
duke@1 | 179 | return 0; |
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 final long getSerialVersionUID() { |
duke@1 | 189 | return suid; |
duke@1 | 190 | } |
duke@1 | 191 | |
duke@1 | 192 | /** |
duke@1 | 193 | * Return the serialVersionUID string for this class. |
duke@1 | 194 | * The serialVersionUID defines a set of classes all with the same name |
duke@1 | 195 | * that have evolved from a common root class and agree to be serialized |
duke@1 | 196 | * and deserialized using a common format. |
duke@1 | 197 | */ |
duke@1 | 198 | public final String getSerialVersionUIDStr() { |
duke@1 | 199 | if (suidStr == null) |
duke@1 | 200 | suidStr = Long.toHexString(suid).toUpperCase(); |
duke@1 | 201 | return suidStr; |
duke@1 | 202 | } |
duke@1 | 203 | |
duke@1 | 204 | /** |
duke@1 | 205 | * Return the actual (computed) serialVersionUID for this class. |
duke@1 | 206 | */ |
duke@1 | 207 | public static final long getActualSerialVersionUID( java.lang.Class clazz ) |
duke@1 | 208 | { |
duke@1 | 209 | ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz ); |
duke@1 | 210 | if( theosc != null ) |
duke@1 | 211 | { |
duke@1 | 212 | return theosc.getActualSerialVersionUID( ); |
duke@1 | 213 | } |
duke@1 | 214 | return 0; |
duke@1 | 215 | } |
duke@1 | 216 | |
duke@1 | 217 | /** |
duke@1 | 218 | * Return the actual (computed) serialVersionUID for this class. |
duke@1 | 219 | */ |
duke@1 | 220 | public final long getActualSerialVersionUID() { |
duke@1 | 221 | return actualSuid; |
duke@1 | 222 | } |
duke@1 | 223 | |
duke@1 | 224 | /** |
duke@1 | 225 | * Return the actual (computed) serialVersionUID for this class. |
duke@1 | 226 | */ |
duke@1 | 227 | public final String getActualSerialVersionUIDStr() { |
duke@1 | 228 | if (actualSuidStr == null) |
duke@1 | 229 | actualSuidStr = Long.toHexString(actualSuid).toUpperCase(); |
duke@1 | 230 | return actualSuidStr; |
duke@1 | 231 | } |
duke@1 | 232 | |
duke@1 | 233 | /** |
duke@1 | 234 | * Return the class in the local VM that this version is mapped to. |
duke@1 | 235 | * Null is returned if there is no corresponding local class. |
duke@1 | 236 | */ |
duke@1 | 237 | public final Class forClass() { |
duke@1 | 238 | return ofClass; |
duke@1 | 239 | } |
duke@1 | 240 | |
duke@1 | 241 | /** |
duke@1 | 242 | * Return an array of the fields of this serializable class. |
duke@1 | 243 | * @return an array containing an element for each persistent |
duke@1 | 244 | * field of this class. Returns an array of length zero if |
duke@1 | 245 | * there are no fields. |
duke@1 | 246 | * @since JDK1.2 |
duke@1 | 247 | */ |
duke@1 | 248 | public ObjectStreamField[] getFields() { |
duke@1 | 249 | // Return a copy so the caller can't change the fields. |
duke@1 | 250 | if (fields.length > 0) { |
duke@1 | 251 | ObjectStreamField[] dup = new ObjectStreamField[fields.length]; |
duke@1 | 252 | System.arraycopy(fields, 0, dup, 0, fields.length); |
duke@1 | 253 | return dup; |
duke@1 | 254 | } else { |
duke@1 | 255 | return fields; |
duke@1 | 256 | } |
duke@1 | 257 | } |
duke@1 | 258 | |
duke@1 | 259 | public boolean hasField(ValueMember field){ |
duke@1 | 260 | |
duke@1 | 261 | for (int i = 0; i < fields.length; i++){ |
duke@1 | 262 | try{ |
duke@1 | 263 | if (fields[i].getName().equals(field.name)) { |
duke@1 | 264 | |
duke@1 | 265 | if (fields[i].getSignature().equals(ValueUtility.getSignature(field))) |
duke@1 | 266 | return true; |
duke@1 | 267 | } |
duke@1 | 268 | } |
duke@1 | 269 | catch(Throwable t){} |
duke@1 | 270 | } |
duke@1 | 271 | return false; |
duke@1 | 272 | } |
duke@1 | 273 | |
duke@1 | 274 | /* Avoid unnecessary allocations. */ |
duke@1 | 275 | final ObjectStreamField[] getFieldsNoCopy() { |
duke@1 | 276 | return fields; |
duke@1 | 277 | } |
duke@1 | 278 | |
duke@1 | 279 | /** |
duke@1 | 280 | * Get the field of this class by name. |
duke@1 | 281 | * @return The ObjectStreamField object of the named field or null if there |
duke@1 | 282 | * is no such named field. |
duke@1 | 283 | */ |
duke@1 | 284 | public final ObjectStreamField getField(String name) { |
duke@1 | 285 | /* Binary search of fields by name. |
duke@1 | 286 | */ |
duke@1 | 287 | for (int i = fields.length-1; i >= 0; i--) { |
duke@1 | 288 | if (name.equals(fields[i].getName())) { |
duke@1 | 289 | return fields[i]; |
duke@1 | 290 | } |
duke@1 | 291 | } |
duke@1 | 292 | return null; |
duke@1 | 293 | } |
duke@1 | 294 | |
duke@1 | 295 | public Serializable writeReplace(Serializable value) { |
duke@1 | 296 | if (writeReplaceObjectMethod != null) { |
duke@1 | 297 | try { |
duke@1 | 298 | return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList); |
duke@1 | 299 | } |
duke@1 | 300 | catch(Throwable t) { |
duke@1 | 301 | throw new RuntimeException(t.getMessage()); |
duke@1 | 302 | } |
duke@1 | 303 | } |
duke@1 | 304 | else return value; |
duke@1 | 305 | } |
duke@1 | 306 | |
duke@1 | 307 | public Object readResolve(Object value) { |
duke@1 | 308 | if (readResolveObjectMethod != null) { |
duke@1 | 309 | try { |
duke@1 | 310 | return readResolveObjectMethod.invoke(value,noArgsList); |
duke@1 | 311 | } |
duke@1 | 312 | catch(Throwable t) { |
duke@1 | 313 | throw new RuntimeException(t.getMessage()); |
duke@1 | 314 | } |
duke@1 | 315 | } |
duke@1 | 316 | else return value; |
duke@1 | 317 | } |
duke@1 | 318 | |
duke@1 | 319 | /** |
duke@1 | 320 | * Return a string describing this ObjectStreamClass_1_3_1. |
duke@1 | 321 | */ |
duke@1 | 322 | public final String toString() { |
duke@1 | 323 | StringBuffer sb = new StringBuffer(); |
duke@1 | 324 | |
duke@1 | 325 | sb.append(name); |
duke@1 | 326 | sb.append(": static final long serialVersionUID = "); |
duke@1 | 327 | sb.append(Long.toString(suid)); |
duke@1 | 328 | sb.append("L;"); |
duke@1 | 329 | return sb.toString(); |
duke@1 | 330 | } |
duke@1 | 331 | |
duke@1 | 332 | /* |
duke@1 | 333 | * Create a new ObjectStreamClass_1_3_1 from a loaded class. |
duke@1 | 334 | * Don't call this directly, call lookup instead. |
duke@1 | 335 | */ |
duke@1 | 336 | private ObjectStreamClass_1_3_1(java.lang.Class cl, ObjectStreamClass_1_3_1 superdesc, |
duke@1 | 337 | boolean serial, boolean extern) |
duke@1 | 338 | { |
duke@1 | 339 | ofClass = cl; /* created from this class */ |
duke@1 | 340 | |
duke@1 | 341 | if (Proxy.isProxyClass(cl)) { |
duke@1 | 342 | forProxyClass = true; |
duke@1 | 343 | } |
duke@1 | 344 | |
duke@1 | 345 | name = cl.getName(); |
duke@1 | 346 | superclass = superdesc; |
duke@1 | 347 | serializable = serial; |
duke@1 | 348 | if (!forProxyClass) { |
duke@1 | 349 | // proxy classes are never externalizable |
duke@1 | 350 | externalizable = extern; |
duke@1 | 351 | } |
duke@1 | 352 | |
duke@1 | 353 | /* |
duke@1 | 354 | * Enter this class in the table of known descriptors. |
duke@1 | 355 | * Otherwise, when the fields are read it may recurse |
duke@1 | 356 | * trying to find the descriptor for itself. |
duke@1 | 357 | */ |
duke@1 | 358 | insertDescriptorFor(this); |
duke@1 | 359 | |
duke@1 | 360 | /* |
duke@1 | 361 | * The remainder of initialization occurs in init(), which is called |
duke@1 | 362 | * after the lock on the global class descriptor table has been |
duke@1 | 363 | * released. |
duke@1 | 364 | */ |
duke@1 | 365 | } |
duke@1 | 366 | |
duke@1 | 367 | /* |
duke@1 | 368 | * Initialize class descriptor. This method is only invoked on class |
duke@1 | 369 | * descriptors created via calls to lookupInternal(). This method is kept |
duke@1 | 370 | * separate from the ObjectStreamClass_1_3_1 constructor so that lookupInternal |
duke@1 | 371 | * does not have to hold onto a global class descriptor table lock while the |
duke@1 | 372 | * class descriptor is being initialized (see bug 4165204). |
duke@1 | 373 | */ |
duke@1 | 374 | |
duke@1 | 375 | |
duke@1 | 376 | private void init() { |
duke@1 | 377 | synchronized (lock) { |
duke@1 | 378 | |
duke@1 | 379 | final Class cl = ofClass; |
duke@1 | 380 | |
duke@1 | 381 | if (fields != null) // already initialized |
duke@1 | 382 | return; |
duke@1 | 383 | |
duke@1 | 384 | |
duke@1 | 385 | if (!serializable || |
duke@1 | 386 | externalizable || |
duke@1 | 387 | forProxyClass || |
duke@1 | 388 | name.equals("java.lang.String")) { |
duke@1 | 389 | fields = NO_FIELDS; |
duke@1 | 390 | } else if (serializable) { |
duke@1 | 391 | |
duke@1 | 392 | /* Ask for permission to override field access checks. |
duke@1 | 393 | */ |
duke@1 | 394 | AccessController.doPrivileged(new PrivilegedAction() { |
duke@1 | 395 | public Object run() { |
duke@1 | 396 | /* Fill in the list of persistent fields. |
duke@1 | 397 | * If it is declared, use the declared serialPersistentFields. |
duke@1 | 398 | * Otherwise, extract the fields from the class itself. |
duke@1 | 399 | */ |
duke@1 | 400 | try { |
duke@1 | 401 | Field pf = cl.getDeclaredField("serialPersistentFields"); |
duke@1 | 402 | // serial bug 7; the serialPersistentFields were not |
duke@1 | 403 | // being read and stored as Accessible bit was not set |
duke@1 | 404 | pf.setAccessible(true); |
duke@1 | 405 | // serial bug 7; need to find if the field is of type |
duke@1 | 406 | // java.io.ObjectStreamField |
duke@1 | 407 | java.io.ObjectStreamField[] f = |
duke@1 | 408 | (java.io.ObjectStreamField[])pf.get(cl); |
duke@1 | 409 | int mods = pf.getModifiers(); |
duke@1 | 410 | if ((Modifier.isPrivate(mods)) && |
duke@1 | 411 | (Modifier.isStatic(mods)) && |
duke@1 | 412 | (Modifier.isFinal(mods))) |
duke@1 | 413 | { |
duke@1 | 414 | fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl)); |
duke@1 | 415 | } |
duke@1 | 416 | } catch (NoSuchFieldException e) { |
duke@1 | 417 | fields = null; |
duke@1 | 418 | } catch (IllegalAccessException e) { |
duke@1 | 419 | fields = null; |
duke@1 | 420 | } catch (IllegalArgumentException e) { |
duke@1 | 421 | fields = null; |
duke@1 | 422 | } catch (ClassCastException e) { |
duke@1 | 423 | /* Thrown if a field serialPersistentField exists |
duke@1 | 424 | * but it is not of type ObjectStreamField. |
duke@1 | 425 | */ |
duke@1 | 426 | fields = null; |
duke@1 | 427 | } |
duke@1 | 428 | |
duke@1 | 429 | |
duke@1 | 430 | if (fields == null) { |
duke@1 | 431 | /* Get all of the declared fields for this |
duke@1 | 432 | * Class. setAccessible on all fields so they |
duke@1 | 433 | * can be accessed later. Create a temporary |
duke@1 | 434 | * ObjectStreamField array to hold each |
duke@1 | 435 | * non-static, non-transient field. Then copy the |
duke@1 | 436 | * temporary array into an array of the correct |
duke@1 | 437 | * size once the number of fields is known. |
duke@1 | 438 | */ |
duke@1 | 439 | Field[] actualfields = cl.getDeclaredFields(); |
duke@1 | 440 | |
duke@1 | 441 | int numFields = 0; |
duke@1 | 442 | ObjectStreamField[] tempFields = |
duke@1 | 443 | new ObjectStreamField[actualfields.length]; |
duke@1 | 444 | for (int i = 0; i < actualfields.length; i++) { |
duke@1 | 445 | int modifiers = actualfields[i].getModifiers(); |
duke@1 | 446 | if (!Modifier.isStatic(modifiers) && |
duke@1 | 447 | !Modifier.isTransient(modifiers)) { |
duke@1 | 448 | tempFields[numFields++] = |
duke@1 | 449 | new ObjectStreamField(actualfields[i]); |
duke@1 | 450 | } |
duke@1 | 451 | } |
duke@1 | 452 | fields = new ObjectStreamField[numFields]; |
duke@1 | 453 | System.arraycopy(tempFields, 0, fields, 0, numFields); |
duke@1 | 454 | |
duke@1 | 455 | } else { |
duke@1 | 456 | // For each declared persistent field, look for an actual |
duke@1 | 457 | // reflected Field. If there is one, make sure it's the correct |
duke@1 | 458 | // type and cache it in the ObjectStreamClass_1_3_1 for that field. |
duke@1 | 459 | for (int j = fields.length-1; j >= 0; j--) { |
duke@1 | 460 | try { |
duke@1 | 461 | Field reflField = cl.getDeclaredField(fields[j].getName()); |
duke@1 | 462 | if (fields[j].getType() == reflField.getType()) { |
duke@1 | 463 | // reflField.setAccessible(true); |
duke@1 | 464 | fields[j].setField(reflField); |
duke@1 | 465 | } |
duke@1 | 466 | } catch (NoSuchFieldException e) { |
duke@1 | 467 | // Nothing to do |
duke@1 | 468 | } |
duke@1 | 469 | } |
duke@1 | 470 | } |
duke@1 | 471 | return null; |
duke@1 | 472 | } |
duke@1 | 473 | }); |
duke@1 | 474 | |
duke@1 | 475 | if (fields.length > 1) |
duke@1 | 476 | Arrays.sort(fields); |
duke@1 | 477 | |
duke@1 | 478 | /* Set up field data for use while writing using the API api. */ |
duke@1 | 479 | computeFieldInfo(); |
duke@1 | 480 | } |
duke@1 | 481 | |
duke@1 | 482 | /* Get the serialVersionUID from the class. |
duke@1 | 483 | * It uses the access override mechanism so make sure |
duke@1 | 484 | * the field objects is only used here. |
duke@1 | 485 | * |
duke@1 | 486 | * NonSerializable classes have a serialVerisonUID of 0L. |
duke@1 | 487 | */ |
duke@1 | 488 | if (isNonSerializable()) { |
duke@1 | 489 | suid = 0L; |
duke@1 | 490 | } else { |
duke@1 | 491 | // Lookup special Serializable members using reflection. |
duke@1 | 492 | AccessController.doPrivileged(new PrivilegedAction() { |
duke@1 | 493 | public Object run() { |
duke@1 | 494 | if (forProxyClass) { |
duke@1 | 495 | // proxy classes always have serialVersionUID of 0L |
duke@1 | 496 | suid = 0L; |
duke@1 | 497 | } else { |
duke@1 | 498 | try { |
duke@1 | 499 | final Field f = cl.getDeclaredField("serialVersionUID"); |
duke@1 | 500 | int mods = f.getModifiers(); |
duke@1 | 501 | // SerialBug 5: static final SUID should be read |
duke@1 | 502 | if (Modifier.isStatic(mods) && |
duke@1 | 503 | Modifier.isFinal(mods) ) { |
duke@1 | 504 | f.setAccessible(true); |
duke@1 | 505 | suid = f.getLong(cl); |
duke@1 | 506 | // get rid of native code |
duke@1 | 507 | // suid = getSerialVersionUIDField(cl); |
duke@1 | 508 | // SerialBug 2: should be computed after writeObject |
duke@1 | 509 | // actualSuid = computeStructuralUID(cl); |
duke@1 | 510 | } else { |
duke@1 | 511 | suid = ObjectStreamClass.getSerialVersionUID(cl); |
duke@1 | 512 | // SerialBug 2: should be computed after writeObject |
duke@1 | 513 | // actualSuid = computeStructuralUID(cl); |
duke@1 | 514 | } |
duke@1 | 515 | } catch (NoSuchFieldException ex) { |
duke@1 | 516 | suid = ObjectStreamClass.getSerialVersionUID(cl); |
duke@1 | 517 | // SerialBug 2: should be computed after writeObject |
duke@1 | 518 | // actualSuid = computeStructuralUID(cl); |
duke@1 | 519 | } catch (IllegalAccessException ex) { |
duke@1 | 520 | suid = ObjectStreamClass.getSerialVersionUID(cl); |
duke@1 | 521 | } |
duke@1 | 522 | } |
duke@1 | 523 | |
duke@1 | 524 | |
duke@1 | 525 | try { |
duke@1 | 526 | writeReplaceObjectMethod = cl.getDeclaredMethod("writeReplace", noTypesList); |
duke@1 | 527 | if (Modifier.isStatic(writeReplaceObjectMethod.getModifiers())) { |
duke@1 | 528 | writeReplaceObjectMethod = null; |
duke@1 | 529 | } else { |
duke@1 | 530 | writeReplaceObjectMethod.setAccessible(true); |
duke@1 | 531 | } |
duke@1 | 532 | |
duke@1 | 533 | } catch (NoSuchMethodException e2) { |
duke@1 | 534 | |
duke@1 | 535 | } |
duke@1 | 536 | |
duke@1 | 537 | try { |
duke@1 | 538 | readResolveObjectMethod = cl.getDeclaredMethod("readResolve", noTypesList); |
duke@1 | 539 | if (Modifier.isStatic(readResolveObjectMethod.getModifiers())) { |
duke@1 | 540 | readResolveObjectMethod = null; |
duke@1 | 541 | } else { |
duke@1 | 542 | readResolveObjectMethod.setAccessible(true); |
duke@1 | 543 | } |
duke@1 | 544 | |
duke@1 | 545 | } catch (NoSuchMethodException e2) { |
duke@1 | 546 | |
duke@1 | 547 | } |
duke@1 | 548 | |
duke@1 | 549 | /* Cache lookup of writeObject and readObject for |
duke@1 | 550 | * Serializable classes. (Do not lookup for |
duke@1 | 551 | * Externalizable) |
duke@1 | 552 | */ |
duke@1 | 553 | |
duke@1 | 554 | if (serializable && !forProxyClass) { |
duke@1 | 555 | |
duke@1 | 556 | /* Look for the writeObject method |
duke@1 | 557 | * Set the accessible flag on it here. ObjectOutputStream |
duke@1 | 558 | * will call it as necessary. |
duke@1 | 559 | */ |
duke@1 | 560 | try { |
duke@1 | 561 | Class[] args = {java.io.ObjectOutputStream.class}; |
duke@1 | 562 | writeObjectMethod = cl.getDeclaredMethod("writeObject", args); |
duke@1 | 563 | hasWriteObjectMethod = true; |
duke@1 | 564 | int mods = writeObjectMethod.getModifiers(); |
duke@1 | 565 | |
duke@1 | 566 | // Method must be private and non-static |
duke@1 | 567 | if (!Modifier.isPrivate(mods) || |
duke@1 | 568 | Modifier.isStatic(mods)) { |
duke@1 | 569 | writeObjectMethod = null; |
duke@1 | 570 | hasWriteObjectMethod = false; |
duke@1 | 571 | } |
duke@1 | 572 | |
duke@1 | 573 | } catch (NoSuchMethodException e) { |
duke@1 | 574 | } |
duke@1 | 575 | |
duke@1 | 576 | /* Look for the readObject method |
duke@1 | 577 | * set the access override and save the reference for |
duke@1 | 578 | * ObjectInputStream so it can all the method directly. |
duke@1 | 579 | */ |
duke@1 | 580 | try { |
duke@1 | 581 | Class[] args = {java.io.ObjectInputStream.class}; |
duke@1 | 582 | readObjectMethod = cl.getDeclaredMethod("readObject", args); |
duke@1 | 583 | int mods = readObjectMethod.getModifiers(); |
duke@1 | 584 | |
duke@1 | 585 | // Method must be private and non-static |
duke@1 | 586 | if (!Modifier.isPrivate(mods) || |
duke@1 | 587 | Modifier.isStatic(mods)) { |
duke@1 | 588 | readObjectMethod = null; |
duke@1 | 589 | } |
duke@1 | 590 | } catch (NoSuchMethodException e) { |
duke@1 | 591 | } |
duke@1 | 592 | // Compute the structural UID. This must be done after the |
duke@1 | 593 | // calculation for writeObject. Fixed 4/20/2000, eea1 |
duke@1 | 594 | // SerialBug 2: to have correct value in RepId |
duke@1 | 595 | } |
duke@1 | 596 | return null; |
duke@1 | 597 | } |
duke@1 | 598 | }); |
duke@1 | 599 | } |
duke@1 | 600 | |
duke@1 | 601 | actualSuid = computeStructuralUID(this, cl); |
duke@1 | 602 | } |
duke@1 | 603 | |
duke@1 | 604 | } |
duke@1 | 605 | |
duke@1 | 606 | /* |
duke@1 | 607 | * Create an empty ObjectStreamClass_1_3_1 for a class about to be read. |
duke@1 | 608 | * This is separate from read so ObjectInputStream can assign the |
duke@1 | 609 | * wire handle early, before any nested ObjectStreamClass_1_3_1 might |
duke@1 | 610 | * be read. |
duke@1 | 611 | */ |
duke@1 | 612 | ObjectStreamClass_1_3_1(String n, long s) { |
duke@1 | 613 | name = n; |
duke@1 | 614 | suid = s; |
duke@1 | 615 | superclass = null; |
duke@1 | 616 | } |
duke@1 | 617 | |
duke@1 | 618 | private static Object[] translateFields(Object objs[]) |
duke@1 | 619 | throws NoSuchFieldException { |
duke@1 | 620 | try{ |
duke@1 | 621 | java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs; |
duke@1 | 622 | Object translation[] = null; |
duke@1 | 623 | |
duke@1 | 624 | if (translatedFields == null) |
duke@1 | 625 | translatedFields = new Hashtable(); |
duke@1 | 626 | |
duke@1 | 627 | translation = (Object[])translatedFields.get(fields); |
duke@1 | 628 | |
duke@1 | 629 | if (translation != null) |
duke@1 | 630 | return translation; |
duke@1 | 631 | else { |
duke@1 | 632 | Class osfClass = com.sun.corba.se.impl.orbutil.ObjectStreamField.class; |
duke@1 | 633 | |
duke@1 | 634 | translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length); |
duke@1 | 635 | Object arg[] = new Object[2]; |
duke@1 | 636 | Class types[] = {String.class, Class.class}; |
duke@1 | 637 | Constructor constructor = osfClass.getDeclaredConstructor(types); |
duke@1 | 638 | for (int i = fields.length -1; i >= 0; i--){ |
duke@1 | 639 | arg[0] = fields[i].getName(); |
duke@1 | 640 | arg[1] = fields[i].getType(); |
duke@1 | 641 | |
duke@1 | 642 | translation[i] = constructor.newInstance(arg); |
duke@1 | 643 | } |
duke@1 | 644 | translatedFields.put(fields, translation); |
duke@1 | 645 | |
duke@1 | 646 | } |
duke@1 | 647 | |
duke@1 | 648 | return (Object[])translation; |
duke@1 | 649 | } |
duke@1 | 650 | catch(Throwable t){ |
duke@1 | 651 | throw new NoSuchFieldException(); |
duke@1 | 652 | } |
duke@1 | 653 | } |
duke@1 | 654 | |
duke@1 | 655 | /* Compare the base class names of streamName and localName. |
duke@1 | 656 | * |
duke@1 | 657 | * @return Return true iff the base class name compare. |
duke@1 | 658 | * @parameter streamName Fully qualified class name. |
duke@1 | 659 | * @parameter localName Fully qualified class name. |
duke@1 | 660 | * @parameter pkgSeparator class names use either '.' or '/'. |
duke@1 | 661 | * |
duke@1 | 662 | * Only compare base class name to allow package renaming. |
duke@1 | 663 | */ |
duke@1 | 664 | static boolean compareClassNames(String streamName, |
duke@1 | 665 | String localName, |
duke@1 | 666 | char pkgSeparator) { |
duke@1 | 667 | /* compare the class names, stripping off package names. */ |
duke@1 | 668 | int streamNameIndex = streamName.lastIndexOf(pkgSeparator); |
duke@1 | 669 | if (streamNameIndex < 0) |
duke@1 | 670 | streamNameIndex = 0; |
duke@1 | 671 | |
duke@1 | 672 | int localNameIndex = localName.lastIndexOf(pkgSeparator); |
duke@1 | 673 | if (localNameIndex < 0) |
duke@1 | 674 | localNameIndex = 0; |
duke@1 | 675 | |
duke@1 | 676 | return streamName.regionMatches(false, streamNameIndex, |
duke@1 | 677 | localName, localNameIndex, |
duke@1 | 678 | streamName.length() - streamNameIndex); |
duke@1 | 679 | } |
duke@1 | 680 | |
duke@1 | 681 | /* |
duke@1 | 682 | * Compare the types of two class descriptors. |
duke@1 | 683 | * They match if they have the same class name and suid |
duke@1 | 684 | */ |
duke@1 | 685 | final boolean typeEquals(ObjectStreamClass_1_3_1 other) { |
duke@1 | 686 | return (suid == other.suid) && |
duke@1 | 687 | compareClassNames(name, other.name, '.'); |
duke@1 | 688 | } |
duke@1 | 689 | |
duke@1 | 690 | /* |
duke@1 | 691 | * Return the superclass descriptor of this descriptor. |
duke@1 | 692 | */ |
duke@1 | 693 | final void setSuperclass(ObjectStreamClass_1_3_1 s) { |
duke@1 | 694 | superclass = s; |
duke@1 | 695 | } |
duke@1 | 696 | |
duke@1 | 697 | /* |
duke@1 | 698 | * Return the superclass descriptor of this descriptor. |
duke@1 | 699 | */ |
duke@1 | 700 | final ObjectStreamClass_1_3_1 getSuperclass() { |
duke@1 | 701 | return superclass; |
duke@1 | 702 | } |
duke@1 | 703 | |
duke@1 | 704 | /* |
duke@1 | 705 | * Return whether the class has a writeObject method |
duke@1 | 706 | */ |
duke@1 | 707 | final boolean hasWriteObject() { |
duke@1 | 708 | return hasWriteObjectMethod; |
duke@1 | 709 | } |
duke@1 | 710 | |
duke@1 | 711 | final boolean isCustomMarshaled() { |
duke@1 | 712 | return (hasWriteObject() || isExternalizable()); |
duke@1 | 713 | } |
duke@1 | 714 | |
duke@1 | 715 | /* |
duke@1 | 716 | * Return true if all instances of 'this' Externalizable class |
duke@1 | 717 | * are written in block-data mode from the stream that 'this' was read |
duke@1 | 718 | * from. <p> |
duke@1 | 719 | * |
duke@1 | 720 | * In JDK 1.1, all Externalizable instances are not written |
duke@1 | 721 | * in block-data mode. |
duke@1 | 722 | * In JDK 1.2, all Externalizable instances, by default, are written |
duke@1 | 723 | * in block-data mode and the Externalizable instance is terminated with |
duke@1 | 724 | * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable |
duke@1 | 725 | * instances. |
duke@1 | 726 | * |
duke@1 | 727 | * IMPLEMENTATION NOTE: |
duke@1 | 728 | * This should have been a mode maintained per stream; however, |
duke@1 | 729 | * for compatibility reasons, it was only possible to record |
duke@1 | 730 | * this change per class. All Externalizable classes within |
duke@1 | 731 | * a given stream should either have this mode enabled or |
duke@1 | 732 | * disabled. This is enforced by not allowing the PROTOCOL_VERSION |
duke@1 | 733 | * of a stream to he changed after any objects have been written. |
duke@1 | 734 | * |
duke@1 | 735 | * @see ObjectOutputStream#useProtocolVersion |
duke@1 | 736 | * @see ObjectStreamConstants#PROTOCOL_VERSION_1 |
duke@1 | 737 | * @see ObjectStreamConstants#PROTOCOL_VERSION_2 |
duke@1 | 738 | * |
duke@1 | 739 | * @since JDK 1.2 |
duke@1 | 740 | */ |
duke@1 | 741 | boolean hasExternalizableBlockDataMode() { |
duke@1 | 742 | return hasExternalizableBlockData; |
duke@1 | 743 | } |
duke@1 | 744 | |
duke@1 | 745 | /* |
duke@1 | 746 | * Return the ObjectStreamClass_1_3_1 of the local class this one is based on. |
duke@1 | 747 | */ |
duke@1 | 748 | final ObjectStreamClass_1_3_1 localClassDescriptor() { |
duke@1 | 749 | return localClassDesc; |
duke@1 | 750 | } |
duke@1 | 751 | |
duke@1 | 752 | /* |
duke@1 | 753 | * Get the Serializability of the class. |
duke@1 | 754 | */ |
duke@1 | 755 | boolean isSerializable() { |
duke@1 | 756 | return serializable; |
duke@1 | 757 | } |
duke@1 | 758 | |
duke@1 | 759 | /* |
duke@1 | 760 | * Get the externalizability of the class. |
duke@1 | 761 | */ |
duke@1 | 762 | boolean isExternalizable() { |
duke@1 | 763 | return externalizable; |
duke@1 | 764 | } |
duke@1 | 765 | |
duke@1 | 766 | boolean isNonSerializable() { |
duke@1 | 767 | return ! (externalizable || serializable); |
duke@1 | 768 | } |
duke@1 | 769 | |
duke@1 | 770 | /* |
duke@1 | 771 | * Calculate the size of the array needed to store primitive data and the |
duke@1 | 772 | * number of object references to read when reading from the input |
duke@1 | 773 | * stream. |
duke@1 | 774 | */ |
duke@1 | 775 | private void computeFieldInfo() { |
duke@1 | 776 | primBytes = 0; |
duke@1 | 777 | objFields = 0; |
duke@1 | 778 | |
duke@1 | 779 | for (int i = 0; i < fields.length; i++ ) { |
duke@1 | 780 | switch (fields[i].getTypeCode()) { |
duke@1 | 781 | case 'B': |
duke@1 | 782 | case 'Z': |
duke@1 | 783 | primBytes += 1; |
duke@1 | 784 | break; |
duke@1 | 785 | case 'C': |
duke@1 | 786 | case 'S': |
duke@1 | 787 | primBytes += 2; |
duke@1 | 788 | break; |
duke@1 | 789 | |
duke@1 | 790 | case 'I': |
duke@1 | 791 | case 'F': |
duke@1 | 792 | primBytes += 4; |
duke@1 | 793 | break; |
duke@1 | 794 | case 'J': |
duke@1 | 795 | case 'D' : |
duke@1 | 796 | primBytes += 8; |
duke@1 | 797 | break; |
duke@1 | 798 | |
duke@1 | 799 | case 'L': |
duke@1 | 800 | case '[': |
duke@1 | 801 | objFields += 1; |
duke@1 | 802 | break; |
duke@1 | 803 | } |
duke@1 | 804 | } |
duke@1 | 805 | } |
duke@1 | 806 | |
duke@1 | 807 | private static long computeStructuralUID(ObjectStreamClass_1_3_1 osc, Class cl) { |
duke@1 | 808 | ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); |
duke@1 | 809 | |
duke@1 | 810 | long h = 0; |
duke@1 | 811 | try { |
duke@1 | 812 | |
duke@1 | 813 | if ((!java.io.Serializable.class.isAssignableFrom(cl)) || |
duke@1 | 814 | (cl.isInterface())){ |
duke@1 | 815 | return 0; |
duke@1 | 816 | } |
duke@1 | 817 | |
duke@1 | 818 | if (java.io.Externalizable.class.isAssignableFrom(cl)) { |
duke@1 | 819 | return 1; |
duke@1 | 820 | } |
duke@1 | 821 | |
duke@1 | 822 | MessageDigest md = MessageDigest.getInstance("SHA"); |
duke@1 | 823 | DigestOutputStream mdo = new DigestOutputStream(devnull, md); |
duke@1 | 824 | DataOutputStream data = new DataOutputStream(mdo); |
duke@1 | 825 | |
duke@1 | 826 | // Get SUID of parent |
duke@1 | 827 | Class parent = cl.getSuperclass(); |
duke@1 | 828 | if ((parent != null)) |
duke@1 | 829 | // SerialBug 1; acc. to spec the one for |
duke@1 | 830 | // java.lang.object |
duke@1 | 831 | // should be computed and put |
duke@1 | 832 | // && (parent != java.lang.Object.class)) |
duke@1 | 833 | { |
duke@1 | 834 | //data.writeLong(computeSerialVersionUID(null,parent)); |
duke@1 | 835 | data.writeLong(computeStructuralUID(lookup(parent), parent)); |
duke@1 | 836 | } |
duke@1 | 837 | |
duke@1 | 838 | if (osc.hasWriteObject()) |
duke@1 | 839 | data.writeInt(2); |
duke@1 | 840 | else |
duke@1 | 841 | data.writeInt(1); |
duke@1 | 842 | |
duke@1 | 843 | /* Sort the field names to get a deterministic order */ |
duke@1 | 844 | // Field[] field = ObjectStreamClass_1_3_1.getDeclaredFields(cl); |
duke@1 | 845 | |
duke@1 | 846 | ObjectStreamField[] fields = osc.getFields(); |
duke@1 | 847 | |
duke@1 | 848 | // Must make sure that the Field array we allocate |
duke@1 | 849 | // below is exactly the right size. Bug fix for |
duke@1 | 850 | // 4397133. |
duke@1 | 851 | int numNonNullFields = 0; |
duke@1 | 852 | for (int i = 0; i < fields.length; i++) |
duke@1 | 853 | if (fields[i].getField() != null) |
duke@1 | 854 | numNonNullFields++; |
duke@1 | 855 | |
duke@1 | 856 | Field [] field = new java.lang.reflect.Field[numNonNullFields]; |
duke@1 | 857 | for (int i = 0, fieldNum = 0; i < fields.length; i++) { |
duke@1 | 858 | if (fields[i].getField() != null) { |
duke@1 | 859 | field[fieldNum++] = fields[i].getField(); |
duke@1 | 860 | } |
duke@1 | 861 | } |
duke@1 | 862 | |
duke@1 | 863 | if (field.length > 1) |
duke@1 | 864 | Arrays.sort(field, compareMemberByName); |
duke@1 | 865 | |
duke@1 | 866 | for (int i = 0; i < field.length; i++) { |
duke@1 | 867 | Field f = field[i]; |
duke@1 | 868 | |
duke@1 | 869 | /* Include in the hash all fields except those that are |
duke@1 | 870 | * transient |
duke@1 | 871 | */ |
duke@1 | 872 | int m = f.getModifiers(); |
duke@1 | 873 | //Serial 6 |
duke@1 | 874 | //if (Modifier.isTransient(m) || Modifier.isStatic(m)) |
duke@1 | 875 | // spec reference 00-01-06.pdf, 1.3.5.6, states non-static |
duke@1 | 876 | // non-transient, public fields are mapped to Java IDL. |
duke@1 | 877 | // |
duke@1 | 878 | // Here's the quote from the first paragraph: |
duke@1 | 879 | // Java non-static non-transient public fields are mapped to |
duke@1 | 880 | // OMG IDL public data members, and other Java fields are |
duke@1 | 881 | // not mapped. |
duke@1 | 882 | |
duke@1 | 883 | // if (Modifier.isTransient(m) || Modifier.isStatic(m)) |
duke@1 | 884 | // continue; |
duke@1 | 885 | |
duke@1 | 886 | data.writeUTF(f.getName()); |
duke@1 | 887 | data.writeUTF(getSignature(f.getType())); |
duke@1 | 888 | } |
duke@1 | 889 | |
duke@1 | 890 | /* Compute the hash value for this class. |
duke@1 | 891 | * Use only the first 64 bits of the hash. |
duke@1 | 892 | */ |
duke@1 | 893 | data.flush(); |
duke@1 | 894 | byte hasharray[] = md.digest(); |
duke@1 | 895 | // int minimum = Math.min(8, hasharray.length); |
duke@1 | 896 | // SerialBug 3: SHA computation is wrong; for loop reversed |
duke@1 | 897 | //for (int i = minimum; i > 0; i--) |
duke@1 | 898 | for (int i = 0; i < Math.min(8, hasharray.length); i++) { |
duke@1 | 899 | h += (long)(hasharray[i] & 255) << (i * 8); |
duke@1 | 900 | } |
duke@1 | 901 | } catch (IOException ignore) { |
duke@1 | 902 | /* can't happen, but be deterministic anyway. */ |
duke@1 | 903 | h = -1; |
duke@1 | 904 | } catch (NoSuchAlgorithmException complain) { |
duke@1 | 905 | throw new SecurityException(complain.getMessage()); |
duke@1 | 906 | } |
duke@1 | 907 | return h; |
duke@1 | 908 | } |
duke@1 | 909 | |
duke@1 | 910 | /** |
duke@1 | 911 | * Compute the JVM signature for the class. |
duke@1 | 912 | */ |
duke@1 | 913 | static String getSignature(Class clazz) { |
duke@1 | 914 | String type = null; |
duke@1 | 915 | if (clazz.isArray()) { |
duke@1 | 916 | Class cl = clazz; |
duke@1 | 917 | int dimensions = 0; |
duke@1 | 918 | while (cl.isArray()) { |
duke@1 | 919 | dimensions++; |
duke@1 | 920 | cl = cl.getComponentType(); |
duke@1 | 921 | } |
duke@1 | 922 | StringBuffer sb = new StringBuffer(); |
duke@1 | 923 | for (int i = 0; i < dimensions; i++) { |
duke@1 | 924 | sb.append("["); |
duke@1 | 925 | } |
duke@1 | 926 | sb.append(getSignature(cl)); |
duke@1 | 927 | type = sb.toString(); |
duke@1 | 928 | } else if (clazz.isPrimitive()) { |
duke@1 | 929 | if (clazz == Integer.TYPE) { |
duke@1 | 930 | type = "I"; |
duke@1 | 931 | } else if (clazz == Byte.TYPE) { |
duke@1 | 932 | type = "B"; |
duke@1 | 933 | } else if (clazz == Long.TYPE) { |
duke@1 | 934 | type = "J"; |
duke@1 | 935 | } else if (clazz == Float.TYPE) { |
duke@1 | 936 | type = "F"; |
duke@1 | 937 | } else if (clazz == Double.TYPE) { |
duke@1 | 938 | type = "D"; |
duke@1 | 939 | } else if (clazz == Short.TYPE) { |
duke@1 | 940 | type = "S"; |
duke@1 | 941 | } else if (clazz == Character.TYPE) { |
duke@1 | 942 | type = "C"; |
duke@1 | 943 | } else if (clazz == Boolean.TYPE) { |
duke@1 | 944 | type = "Z"; |
duke@1 | 945 | } else if (clazz == Void.TYPE) { |
duke@1 | 946 | type = "V"; |
duke@1 | 947 | } |
duke@1 | 948 | } else { |
duke@1 | 949 | type = "L" + clazz.getName().replace('.', '/') + ";"; |
duke@1 | 950 | } |
duke@1 | 951 | return type; |
duke@1 | 952 | } |
duke@1 | 953 | |
duke@1 | 954 | /* |
duke@1 | 955 | * Compute the JVM method descriptor for the method. |
duke@1 | 956 | */ |
duke@1 | 957 | static String getSignature(Method meth) { |
duke@1 | 958 | StringBuffer sb = new StringBuffer(); |
duke@1 | 959 | |
duke@1 | 960 | sb.append("("); |
duke@1 | 961 | |
duke@1 | 962 | Class[] params = meth.getParameterTypes(); // avoid clone |
duke@1 | 963 | for (int j = 0; j < params.length; j++) { |
duke@1 | 964 | sb.append(getSignature(params[j])); |
duke@1 | 965 | } |
duke@1 | 966 | sb.append(")"); |
duke@1 | 967 | sb.append(getSignature(meth.getReturnType())); |
duke@1 | 968 | return sb.toString(); |
duke@1 | 969 | } |
duke@1 | 970 | |
duke@1 | 971 | /* |
duke@1 | 972 | * Compute the JVM constructor descriptor for the constructor. |
duke@1 | 973 | */ |
duke@1 | 974 | static String getSignature(Constructor cons) { |
duke@1 | 975 | StringBuffer sb = new StringBuffer(); |
duke@1 | 976 | |
duke@1 | 977 | sb.append("("); |
duke@1 | 978 | |
duke@1 | 979 | Class[] params = cons.getParameterTypes(); // avoid clone |
duke@1 | 980 | for (int j = 0; j < params.length; j++) { |
duke@1 | 981 | sb.append(getSignature(params[j])); |
duke@1 | 982 | } |
duke@1 | 983 | sb.append(")V"); |
duke@1 | 984 | return sb.toString(); |
duke@1 | 985 | } |
duke@1 | 986 | |
duke@1 | 987 | /* |
duke@1 | 988 | * Cache of Class -> ClassDescriptor Mappings. |
duke@1 | 989 | */ |
duke@1 | 990 | static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61]; |
duke@1 | 991 | |
duke@1 | 992 | /* |
duke@1 | 993 | * findDescriptorFor a Class. This looks in the cache for a |
duke@1 | 994 | * mapping from Class -> ObjectStreamClass mappings. The hashCode |
duke@1 | 995 | * of the Class is used for the lookup since the Class is the key. |
duke@1 | 996 | * The entries are extended from java.lang.ref.SoftReference so the |
duke@1 | 997 | * gc will be able to free them if needed. |
duke@1 | 998 | */ |
duke@1 | 999 | private static ObjectStreamClass_1_3_1 findDescriptorFor(Class cl) { |
duke@1 | 1000 | |
duke@1 | 1001 | int hash = cl.hashCode(); |
duke@1 | 1002 | int index = (hash & 0x7FFFFFFF) % descriptorFor.length; |
duke@1 | 1003 | ObjectStreamClassEntry e; |
duke@1 | 1004 | ObjectStreamClassEntry prev; |
duke@1 | 1005 | |
duke@1 | 1006 | /* Free any initial entries whose refs have been cleared */ |
duke@1 | 1007 | while ((e = descriptorFor[index]) != null && e.get() == null) { |
duke@1 | 1008 | descriptorFor[index] = e.next; |
duke@1 | 1009 | } |
duke@1 | 1010 | |
duke@1 | 1011 | /* Traverse the chain looking for a descriptor with ofClass == cl. |
duke@1 | 1012 | * unlink entries that are unresolved. |
duke@1 | 1013 | */ |
duke@1 | 1014 | prev = e; |
duke@1 | 1015 | while (e != null ) { |
duke@1 | 1016 | ObjectStreamClass_1_3_1 desc = (ObjectStreamClass_1_3_1)(e.get()); |
duke@1 | 1017 | if (desc == null) { |
duke@1 | 1018 | // This entry has been cleared, unlink it |
duke@1 | 1019 | prev.next = e.next; |
duke@1 | 1020 | } else { |
duke@1 | 1021 | if (desc.ofClass == cl) |
duke@1 | 1022 | return desc; |
duke@1 | 1023 | prev = e; |
duke@1 | 1024 | } |
duke@1 | 1025 | e = e.next; |
duke@1 | 1026 | } |
duke@1 | 1027 | return null; |
duke@1 | 1028 | } |
duke@1 | 1029 | |
duke@1 | 1030 | /* |
duke@1 | 1031 | * insertDescriptorFor a Class -> ObjectStreamClass_1_3_1 mapping. |
duke@1 | 1032 | */ |
duke@1 | 1033 | private static void insertDescriptorFor(ObjectStreamClass_1_3_1 desc) { |
duke@1 | 1034 | // Make sure not already present |
duke@1 | 1035 | if (findDescriptorFor(desc.ofClass) != null) { |
duke@1 | 1036 | return; |
duke@1 | 1037 | } |
duke@1 | 1038 | |
duke@1 | 1039 | int hash = desc.ofClass.hashCode(); |
duke@1 | 1040 | int index = (hash & 0x7FFFFFFF) % descriptorFor.length; |
duke@1 | 1041 | ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc); |
duke@1 | 1042 | e.next = descriptorFor[index]; |
duke@1 | 1043 | descriptorFor[index] = e; |
duke@1 | 1044 | } |
duke@1 | 1045 | |
duke@1 | 1046 | private static Field[] getDeclaredFields(final Class clz) { |
duke@1 | 1047 | return (Field[]) AccessController.doPrivileged(new PrivilegedAction() { |
duke@1 | 1048 | public Object run() { |
duke@1 | 1049 | return clz.getDeclaredFields(); |
duke@1 | 1050 | } |
duke@1 | 1051 | }); |
duke@1 | 1052 | } |
duke@1 | 1053 | |
duke@1 | 1054 | |
duke@1 | 1055 | /* |
duke@1 | 1056 | * The name of this descriptor |
duke@1 | 1057 | */ |
duke@1 | 1058 | private String name; |
duke@1 | 1059 | |
duke@1 | 1060 | /* |
duke@1 | 1061 | * The descriptor of the supertype. |
duke@1 | 1062 | */ |
duke@1 | 1063 | private ObjectStreamClass_1_3_1 superclass; |
duke@1 | 1064 | |
duke@1 | 1065 | /* |
duke@1 | 1066 | * Flags for Serializable and Externalizable. |
duke@1 | 1067 | */ |
duke@1 | 1068 | private boolean serializable; |
duke@1 | 1069 | private boolean externalizable; |
duke@1 | 1070 | |
duke@1 | 1071 | /* |
duke@1 | 1072 | * Array of persistent fields of this class, sorted by |
duke@1 | 1073 | * type and name. |
duke@1 | 1074 | */ |
duke@1 | 1075 | private ObjectStreamField[] fields; |
duke@1 | 1076 | |
duke@1 | 1077 | /* |
duke@1 | 1078 | * Class that is a descriptor for in this virtual machine. |
duke@1 | 1079 | */ |
duke@1 | 1080 | private Class ofClass; |
duke@1 | 1081 | |
duke@1 | 1082 | /* |
duke@1 | 1083 | * True if descriptor for a proxy class. |
duke@1 | 1084 | */ |
duke@1 | 1085 | boolean forProxyClass; |
duke@1 | 1086 | |
duke@1 | 1087 | |
duke@1 | 1088 | /* |
duke@1 | 1089 | * SerialVersionUID for this class. |
duke@1 | 1090 | */ |
duke@1 | 1091 | private long suid = kDefaultUID; |
duke@1 | 1092 | private String suidStr = null; |
duke@1 | 1093 | |
duke@1 | 1094 | /* |
duke@1 | 1095 | * Actual (computed) SerialVersionUID for this class. |
duke@1 | 1096 | */ |
duke@1 | 1097 | private long actualSuid = kDefaultUID; |
duke@1 | 1098 | private String actualSuidStr = null; |
duke@1 | 1099 | |
duke@1 | 1100 | /* |
duke@1 | 1101 | * The total number of bytes of primitive fields. |
duke@1 | 1102 | * The total number of object fields. |
duke@1 | 1103 | */ |
duke@1 | 1104 | int primBytes; |
duke@1 | 1105 | int objFields; |
duke@1 | 1106 | |
duke@1 | 1107 | /* Internal lock object. */ |
duke@1 | 1108 | private Object lock = new Object(); |
duke@1 | 1109 | |
duke@1 | 1110 | /* True if this class has/had a writeObject method */ |
duke@1 | 1111 | private boolean hasWriteObjectMethod; |
duke@1 | 1112 | |
duke@1 | 1113 | /* In JDK 1.1, external data was not written in block mode. |
duke@1 | 1114 | * As of JDK 1.2, external data is written in block data mode. This |
duke@1 | 1115 | * flag enables JDK 1.2 to be able to read JDK 1.1 written external data. |
duke@1 | 1116 | * |
duke@1 | 1117 | * @since JDK 1.2 |
duke@1 | 1118 | */ |
duke@1 | 1119 | private boolean hasExternalizableBlockData; |
duke@1 | 1120 | Method writeObjectMethod; |
duke@1 | 1121 | Method readObjectMethod; |
coffeys@446 | 1122 | private transient Method writeReplaceObjectMethod; |
coffeys@446 | 1123 | private transient Method readResolveObjectMethod; |
duke@1 | 1124 | |
duke@1 | 1125 | /* |
duke@1 | 1126 | * ObjectStreamClass_1_3_1 that this one was built from. |
duke@1 | 1127 | */ |
duke@1 | 1128 | private ObjectStreamClass_1_3_1 localClassDesc; |
duke@1 | 1129 | |
duke@1 | 1130 | /* Get the private static final field for serial version UID */ |
duke@1 | 1131 | // private static native long getSerialVersionUIDField(Class cl); |
duke@1 | 1132 | |
duke@1 | 1133 | /* The Class Object for java.io.Serializable */ |
duke@1 | 1134 | private static Class classSerializable = null; |
duke@1 | 1135 | private static Class classExternalizable = null; |
duke@1 | 1136 | |
duke@1 | 1137 | /* |
duke@1 | 1138 | * Resolve java.io.Serializable at load time. |
duke@1 | 1139 | */ |
duke@1 | 1140 | static { |
duke@1 | 1141 | try { |
duke@1 | 1142 | classSerializable = Class.forName("java.io.Serializable"); |
duke@1 | 1143 | classExternalizable = Class.forName("java.io.Externalizable"); |
duke@1 | 1144 | } catch (Throwable e) { |
duke@1 | 1145 | System.err.println("Could not load java.io.Serializable or java.io.Externalizable."); |
duke@1 | 1146 | } |
duke@1 | 1147 | } |
duke@1 | 1148 | |
duke@1 | 1149 | /** use serialVersionUID from JDK 1.1. for interoperability */ |
duke@1 | 1150 | private static final long serialVersionUID = -6120832682080437368L; |
duke@1 | 1151 | |
duke@1 | 1152 | /** |
duke@1 | 1153 | * Set serialPersistentFields of a Serializable class to this value to |
duke@1 | 1154 | * denote that the class has no Serializable fields. |
duke@1 | 1155 | */ |
duke@1 | 1156 | public static final ObjectStreamField[] NO_FIELDS = |
duke@1 | 1157 | new ObjectStreamField[0]; |
duke@1 | 1158 | |
duke@1 | 1159 | /* |
duke@1 | 1160 | * Entries held in the Cache of known ObjectStreamClass_1_3_1 objects. |
duke@1 | 1161 | * Entries are chained together with the same hash value (modulo array size). |
duke@1 | 1162 | */ |
duke@1 | 1163 | private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference |
duke@1 | 1164 | { |
duke@1 | 1165 | ObjectStreamClassEntry(ObjectStreamClass_1_3_1 c) { |
duke@1 | 1166 | //super(c); |
duke@1 | 1167 | this.c = c; |
duke@1 | 1168 | } |
duke@1 | 1169 | ObjectStreamClassEntry next; |
duke@1 | 1170 | |
duke@1 | 1171 | public Object get() |
duke@1 | 1172 | { |
duke@1 | 1173 | return c; |
duke@1 | 1174 | } |
duke@1 | 1175 | private ObjectStreamClass_1_3_1 c; |
duke@1 | 1176 | } |
duke@1 | 1177 | |
duke@1 | 1178 | /* |
duke@1 | 1179 | * Comparator object for Classes and Interfaces |
duke@1 | 1180 | */ |
duke@1 | 1181 | private static Comparator compareClassByName = |
duke@1 | 1182 | new CompareClassByName(); |
duke@1 | 1183 | |
duke@1 | 1184 | private static class CompareClassByName implements Comparator { |
duke@1 | 1185 | public int compare(Object o1, Object o2) { |
duke@1 | 1186 | Class c1 = (Class)o1; |
duke@1 | 1187 | Class c2 = (Class)o2; |
duke@1 | 1188 | return (c1.getName()).compareTo(c2.getName()); |
duke@1 | 1189 | } |
duke@1 | 1190 | } |
duke@1 | 1191 | |
duke@1 | 1192 | /* |
duke@1 | 1193 | * Comparator object for Members, Fields, and Methods |
duke@1 | 1194 | */ |
duke@1 | 1195 | private static Comparator compareMemberByName = |
duke@1 | 1196 | new CompareMemberByName(); |
duke@1 | 1197 | |
duke@1 | 1198 | private static class CompareMemberByName implements Comparator { |
duke@1 | 1199 | public int compare(Object o1, Object o2) { |
duke@1 | 1200 | String s1 = ((Member)o1).getName(); |
duke@1 | 1201 | String s2 = ((Member)o2).getName(); |
duke@1 | 1202 | |
duke@1 | 1203 | if (o1 instanceof Method) { |
duke@1 | 1204 | s1 += getSignature((Method)o1); |
duke@1 | 1205 | s2 += getSignature((Method)o2); |
duke@1 | 1206 | } else if (o1 instanceof Constructor) { |
duke@1 | 1207 | s1 += getSignature((Constructor)o1); |
duke@1 | 1208 | s2 += getSignature((Constructor)o2); |
duke@1 | 1209 | } |
duke@1 | 1210 | return s1.compareTo(s2); |
duke@1 | 1211 | } |
duke@1 | 1212 | } |
duke@1 | 1213 | |
duke@1 | 1214 | /* It is expensive to recompute a method or constructor signature |
duke@1 | 1215 | many times, so compute it only once using this data structure. */ |
duke@1 | 1216 | private static class MethodSignature implements Comparator { |
duke@1 | 1217 | Member member; |
duke@1 | 1218 | String signature; // cached parameter signature |
duke@1 | 1219 | |
duke@1 | 1220 | /* Given an array of Method or Constructor members, |
duke@1 | 1221 | return a sorted array of the non-private members.*/ |
duke@1 | 1222 | /* A better implementation would be to implement the returned data |
duke@1 | 1223 | structure as an insertion sorted link list.*/ |
duke@1 | 1224 | static MethodSignature[] removePrivateAndSort(Member[] m) { |
duke@1 | 1225 | int numNonPrivate = 0; |
duke@1 | 1226 | for (int i = 0; i < m.length; i++) { |
duke@1 | 1227 | if (! Modifier.isPrivate(m[i].getModifiers())) { |
duke@1 | 1228 | numNonPrivate++; |
duke@1 | 1229 | } |
duke@1 | 1230 | } |
duke@1 | 1231 | MethodSignature[] cm = new MethodSignature[numNonPrivate]; |
duke@1 | 1232 | int cmi = 0; |
duke@1 | 1233 | for (int i = 0; i < m.length; i++) { |
duke@1 | 1234 | if (! Modifier.isPrivate(m[i].getModifiers())) { |
duke@1 | 1235 | cm[cmi] = new MethodSignature(m[i]); |
duke@1 | 1236 | cmi++; |
duke@1 | 1237 | } |
duke@1 | 1238 | } |
duke@1 | 1239 | if (cmi > 0) |
duke@1 | 1240 | Arrays.sort(cm, cm[0]); |
duke@1 | 1241 | return cm; |
duke@1 | 1242 | } |
duke@1 | 1243 | |
duke@1 | 1244 | /* Assumes that o1 and o2 are either both methods |
duke@1 | 1245 | or both constructors.*/ |
duke@1 | 1246 | public int compare(Object o1, Object o2) { |
duke@1 | 1247 | /* Arrays.sort calls compare when o1 and o2 are equal.*/ |
duke@1 | 1248 | if (o1 == o2) |
duke@1 | 1249 | return 0; |
duke@1 | 1250 | |
duke@1 | 1251 | MethodSignature c1 = (MethodSignature)o1; |
duke@1 | 1252 | MethodSignature c2 = (MethodSignature)o2; |
duke@1 | 1253 | |
duke@1 | 1254 | int result; |
duke@1 | 1255 | if (isConstructor()) { |
duke@1 | 1256 | result = c1.signature.compareTo(c2.signature); |
duke@1 | 1257 | } else { // is a Method. |
duke@1 | 1258 | result = c1.member.getName().compareTo(c2.member.getName()); |
duke@1 | 1259 | if (result == 0) |
duke@1 | 1260 | result = c1.signature.compareTo(c2.signature); |
duke@1 | 1261 | } |
duke@1 | 1262 | return result; |
duke@1 | 1263 | } |
duke@1 | 1264 | |
duke@1 | 1265 | final private boolean isConstructor() { |
duke@1 | 1266 | return member instanceof Constructor; |
duke@1 | 1267 | } |
duke@1 | 1268 | private MethodSignature(Member m) { |
duke@1 | 1269 | member = m; |
duke@1 | 1270 | if (isConstructor()) { |
duke@1 | 1271 | signature = ObjectStreamClass_1_3_1.getSignature((Constructor)m); |
duke@1 | 1272 | } else { |
duke@1 | 1273 | signature = ObjectStreamClass_1_3_1.getSignature((Method)m); |
duke@1 | 1274 | } |
duke@1 | 1275 | } |
duke@1 | 1276 | } |
duke@1 | 1277 | } |