1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/corba/se/impl/io/ObjectStreamClass.java Sat Dec 01 00:00:00 2007 +0000 1.3 @@ -0,0 +1,1836 @@ 1.4 +/* 1.5 + * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Sun designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Sun in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.25 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.26 + * have any questions. 1.27 + */ 1.28 +/* 1.29 + * Licensed Materials - Property of IBM 1.30 + * RMI-IIOP v1.0 1.31 + * Copyright IBM Corp. 1998 1999 All Rights Reserved 1.32 + * 1.33 + */ 1.34 + 1.35 +package com.sun.corba.se.impl.io; 1.36 + 1.37 +import java.security.MessageDigest; 1.38 +import java.security.NoSuchAlgorithmException; 1.39 +import java.security.DigestOutputStream; 1.40 +import java.security.AccessController; 1.41 +import java.security.PrivilegedExceptionAction; 1.42 +import java.security.PrivilegedActionException; 1.43 +import java.security.PrivilegedAction; 1.44 + 1.45 +import java.lang.reflect.Modifier; 1.46 +import java.lang.reflect.Array; 1.47 +import java.lang.reflect.Field; 1.48 +import java.lang.reflect.Member; 1.49 +import java.lang.reflect.Method; 1.50 +import java.lang.reflect.Constructor; 1.51 +import java.lang.reflect.Proxy; 1.52 +import java.lang.reflect.InvocationTargetException; 1.53 + 1.54 +import java.io.IOException; 1.55 +import java.io.DataOutputStream; 1.56 +import java.io.ByteArrayOutputStream; 1.57 +import java.io.InvalidClassException; 1.58 +import java.io.Serializable; 1.59 + 1.60 +import java.util.Arrays; 1.61 +import java.util.Comparator; 1.62 +import java.util.Hashtable; 1.63 + 1.64 +import com.sun.corba.se.impl.util.RepositoryId; 1.65 + 1.66 +import org.omg.CORBA.ValueMember; 1.67 + 1.68 +import sun.corba.Bridge; 1.69 + 1.70 +/** 1.71 + * A ObjectStreamClass describes a class that can be serialized to a stream 1.72 + * or a class that was serialized to a stream. It contains the name 1.73 + * and the serialVersionUID of the class. 1.74 + * <br> 1.75 + * The ObjectStreamClass for a specific class loaded in this Java VM can 1.76 + * be found using the lookup method. 1.77 + * 1.78 + * @author Roger Riggs 1.79 + * @since JDK1.1 1.80 + */ 1.81 +public class ObjectStreamClass implements java.io.Serializable { 1.82 + private static final boolean DEBUG_SVUID = false ; 1.83 + 1.84 + public static final long kDefaultUID = -1; 1.85 + 1.86 + private static Object noArgsList[] = {}; 1.87 + private static Class noTypesList[] = {}; 1.88 + 1.89 + private static Hashtable translatedFields; 1.90 + 1.91 + private static final Bridge bridge = 1.92 + (Bridge)AccessController.doPrivileged( 1.93 + new PrivilegedAction() { 1.94 + public Object run() { 1.95 + return Bridge.get() ; 1.96 + } 1.97 + } 1.98 + ) ; 1.99 + 1.100 + /** Find the descriptor for a class that can be serialized. Null 1.101 + * is returned if the specified class does not implement 1.102 + * java.io.Serializable or java.io.Externalizable. 1.103 + */ 1.104 + static final ObjectStreamClass lookup(Class cl) 1.105 + { 1.106 + ObjectStreamClass desc = lookupInternal(cl); 1.107 + if (desc.isSerializable() || desc.isExternalizable()) 1.108 + return desc; 1.109 + return null; 1.110 + } 1.111 + 1.112 + /* 1.113 + * Find the class descriptor for the specified class. 1.114 + * Package access only so it can be called from ObjectIn/OutStream. 1.115 + */ 1.116 + static ObjectStreamClass lookupInternal(Class cl) 1.117 + { 1.118 + /* Synchronize on the hashtable so no two threads will do 1.119 + * this at the same time. 1.120 + */ 1.121 + ObjectStreamClass desc = null; 1.122 + synchronized (descriptorFor) { 1.123 + /* Find the matching descriptor if it already known */ 1.124 + desc = findDescriptorFor(cl); 1.125 + if (desc == null) { 1.126 + /* Check if it's serializable */ 1.127 + boolean serializable = classSerializable.isAssignableFrom(cl); 1.128 + 1.129 + /* If the class is only Serializable, 1.130 + * lookup the descriptor for the superclass. 1.131 + */ 1.132 + ObjectStreamClass superdesc = null; 1.133 + if (serializable) { 1.134 + Class superclass = cl.getSuperclass(); 1.135 + if (superclass != null) 1.136 + superdesc = lookup(superclass); 1.137 + } 1.138 + 1.139 + /* Check if its' externalizable. 1.140 + * If it's Externalizable, clear the serializable flag. 1.141 + * Only one or the other may be set in the protocol. 1.142 + */ 1.143 + boolean externalizable = false; 1.144 + if (serializable) { 1.145 + externalizable = 1.146 + ((superdesc != null) && superdesc.isExternalizable()) || 1.147 + classExternalizable.isAssignableFrom(cl); 1.148 + if (externalizable) { 1.149 + serializable = false; 1.150 + } 1.151 + } 1.152 + 1.153 + /* Create a new version descriptor, 1.154 + * it put itself in the known table. 1.155 + */ 1.156 + desc = new ObjectStreamClass(cl, superdesc, 1.157 + serializable, externalizable); 1.158 + } 1.159 + } 1.160 + 1.161 + // Must always call init. See bug 4488137. This code was 1.162 + // incorrectly changed to return immediately on a non-null 1.163 + // cache result. That allowed threads to gain access to 1.164 + // unintialized instances. 1.165 + // 1.166 + // All threads must sync on the member variable lock 1.167 + // and check the initialization state. 1.168 + // 1.169 + // Another possibility is to continue to synchronize on the 1.170 + // descriptorFor array, but that leads to poor performance 1.171 + // (see bug 4165204 "ObjectStreamClass can hold global lock 1.172 + // for a very long time"). 1.173 + desc.init(); 1.174 + 1.175 + return desc; 1.176 + } 1.177 + 1.178 + /** 1.179 + * The name of the class described by this descriptor. 1.180 + */ 1.181 + public final String getName() { 1.182 + return name; 1.183 + } 1.184 + 1.185 + /** 1.186 + * Return the serialVersionUID for this class. 1.187 + * The serialVersionUID defines a set of classes all with the same name 1.188 + * that have evolved from a common root class and agree to be serialized 1.189 + * and deserialized using a common format. 1.190 + */ 1.191 + public static final long getSerialVersionUID( java.lang.Class clazz) { 1.192 + ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz ); 1.193 + if( theosc != null ) 1.194 + { 1.195 + return theosc.getSerialVersionUID( ); 1.196 + } 1.197 + return 0; 1.198 + } 1.199 + 1.200 + /** 1.201 + * Return the serialVersionUID for this class. 1.202 + * The serialVersionUID defines a set of classes all with the same name 1.203 + * that have evolved from a common root class and agree to be serialized 1.204 + * and deserialized using a common format. 1.205 + */ 1.206 + public final long getSerialVersionUID() { 1.207 + return suid; 1.208 + } 1.209 + 1.210 + /** 1.211 + * Return the serialVersionUID string for this class. 1.212 + * The serialVersionUID defines a set of classes all with the same name 1.213 + * that have evolved from a common root class and agree to be serialized 1.214 + * and deserialized using a common format. 1.215 + */ 1.216 + public final String getSerialVersionUIDStr() { 1.217 + if (suidStr == null) 1.218 + suidStr = Long.toHexString(suid).toUpperCase(); 1.219 + return suidStr; 1.220 + } 1.221 + 1.222 + /** 1.223 + * Return the actual (computed) serialVersionUID for this class. 1.224 + */ 1.225 + public static final long getActualSerialVersionUID( java.lang.Class clazz ) 1.226 + { 1.227 + ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz ); 1.228 + if( theosc != null ) 1.229 + { 1.230 + return theosc.getActualSerialVersionUID( ); 1.231 + } 1.232 + return 0; 1.233 + } 1.234 + 1.235 + /** 1.236 + * Return the actual (computed) serialVersionUID for this class. 1.237 + */ 1.238 + public final long getActualSerialVersionUID() { 1.239 + return actualSuid; 1.240 + } 1.241 + 1.242 + /** 1.243 + * Return the actual (computed) serialVersionUID for this class. 1.244 + */ 1.245 + public final String getActualSerialVersionUIDStr() { 1.246 + if (actualSuidStr == null) 1.247 + actualSuidStr = Long.toHexString(actualSuid).toUpperCase(); 1.248 + return actualSuidStr; 1.249 + } 1.250 + 1.251 + /** 1.252 + * Return the class in the local VM that this version is mapped to. 1.253 + * Null is returned if there is no corresponding local class. 1.254 + */ 1.255 + public final Class forClass() { 1.256 + return ofClass; 1.257 + } 1.258 + 1.259 + /** 1.260 + * Return an array of the fields of this serializable class. 1.261 + * @return an array containing an element for each persistent 1.262 + * field of this class. Returns an array of length zero if 1.263 + * there are no fields. 1.264 + * @since JDK1.2 1.265 + */ 1.266 + public ObjectStreamField[] getFields() { 1.267 + // Return a copy so the caller can't change the fields. 1.268 + if (fields.length > 0) { 1.269 + ObjectStreamField[] dup = new ObjectStreamField[fields.length]; 1.270 + System.arraycopy(fields, 0, dup, 0, fields.length); 1.271 + return dup; 1.272 + } else { 1.273 + return fields; 1.274 + } 1.275 + } 1.276 + 1.277 + public boolean hasField(ValueMember field) 1.278 + { 1.279 + try { 1.280 + for (int i = 0; i < fields.length; i++) { 1.281 + if (fields[i].getName().equals(field.name)) { 1.282 + if (fields[i].getSignature().equals( 1.283 + ValueUtility.getSignature(field))) 1.284 + return true; 1.285 + } 1.286 + } 1.287 + } catch (Exception exc) { 1.288 + // Ignore this; all we want to do is return false 1.289 + // Note that ValueUtility.getSignature can throw checked exceptions. 1.290 + } 1.291 + 1.292 + return false; 1.293 + } 1.294 + 1.295 + /* Avoid unnecessary allocations. */ 1.296 + final ObjectStreamField[] getFieldsNoCopy() { 1.297 + return fields; 1.298 + } 1.299 + 1.300 + /** 1.301 + * Get the field of this class by name. 1.302 + * @return The ObjectStreamField object of the named field or null if there 1.303 + * is no such named field. 1.304 + */ 1.305 + public final ObjectStreamField getField(String name) { 1.306 + /* Binary search of fields by name. 1.307 + */ 1.308 + for (int i = fields.length-1; i >= 0; i--) { 1.309 + if (name.equals(fields[i].getName())) { 1.310 + return fields[i]; 1.311 + } 1.312 + } 1.313 + return null; 1.314 + } 1.315 + 1.316 + public Serializable writeReplace(Serializable value) { 1.317 + if (writeReplaceObjectMethod != null) { 1.318 + try { 1.319 + return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList); 1.320 + } catch(Throwable t) { 1.321 + throw new RuntimeException(t); 1.322 + } 1.323 + } 1.324 + else return value; 1.325 + } 1.326 + 1.327 + public Object readResolve(Object value) { 1.328 + if (readResolveObjectMethod != null) { 1.329 + try { 1.330 + return readResolveObjectMethod.invoke(value,noArgsList); 1.331 + } catch(Throwable t) { 1.332 + throw new RuntimeException(t); 1.333 + } 1.334 + } 1.335 + else return value; 1.336 + } 1.337 + 1.338 + /** 1.339 + * Return a string describing this ObjectStreamClass. 1.340 + */ 1.341 + public final String toString() { 1.342 + StringBuffer sb = new StringBuffer(); 1.343 + 1.344 + sb.append(name); 1.345 + sb.append(": static final long serialVersionUID = "); 1.346 + sb.append(Long.toString(suid)); 1.347 + sb.append("L;"); 1.348 + return sb.toString(); 1.349 + } 1.350 + 1.351 + /* 1.352 + * Create a new ObjectStreamClass from a loaded class. 1.353 + * Don't call this directly, call lookup instead. 1.354 + */ 1.355 + private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc, 1.356 + boolean serial, boolean extern) 1.357 + { 1.358 + ofClass = cl; /* created from this class */ 1.359 + 1.360 + if (Proxy.isProxyClass(cl)) { 1.361 + forProxyClass = true; 1.362 + } 1.363 + 1.364 + name = cl.getName(); 1.365 + superclass = superdesc; 1.366 + serializable = serial; 1.367 + if (!forProxyClass) { 1.368 + // proxy classes are never externalizable 1.369 + externalizable = extern; 1.370 + } 1.371 + 1.372 + /* 1.373 + * Enter this class in the table of known descriptors. 1.374 + * Otherwise, when the fields are read it may recurse 1.375 + * trying to find the descriptor for itself. 1.376 + */ 1.377 + insertDescriptorFor(this); 1.378 + 1.379 + /* 1.380 + * The remainder of initialization occurs in init(), which is called 1.381 + * after the lock on the global class descriptor table has been 1.382 + * released. 1.383 + */ 1.384 + } 1.385 + 1.386 + /* 1.387 + * Initialize class descriptor. This method is only invoked on class 1.388 + * descriptors created via calls to lookupInternal(). This method is kept 1.389 + * separate from the ObjectStreamClass constructor so that lookupInternal 1.390 + * does not have to hold onto a global class descriptor table lock while the 1.391 + * class descriptor is being initialized (see bug 4165204). 1.392 + */ 1.393 + 1.394 + 1.395 + private void init() { 1.396 + synchronized (lock) { 1.397 + 1.398 + // See description at definition of initialized. 1.399 + if (initialized) 1.400 + return; 1.401 + 1.402 + final Class cl = ofClass; 1.403 + 1.404 + if (!serializable || 1.405 + externalizable || 1.406 + forProxyClass || 1.407 + name.equals("java.lang.String")) { 1.408 + fields = NO_FIELDS; 1.409 + } else if (serializable) { 1.410 + /* Ask for permission to override field access checks. 1.411 + */ 1.412 + AccessController.doPrivileged(new PrivilegedAction() { 1.413 + public Object run() { 1.414 + /* Fill in the list of persistent fields. 1.415 + * If it is declared, use the declared serialPersistentFields. 1.416 + * Otherwise, extract the fields from the class itself. 1.417 + */ 1.418 + try { 1.419 + Field pf = cl.getDeclaredField("serialPersistentFields"); 1.420 + // serial bug 7; the serialPersistentFields were not 1.421 + // being read and stored as Accessible bit was not set 1.422 + pf.setAccessible(true); 1.423 + // serial bug 7; need to find if the field is of type 1.424 + // java.io.ObjectStreamField 1.425 + java.io.ObjectStreamField[] f = 1.426 + (java.io.ObjectStreamField[])pf.get(cl); 1.427 + int mods = pf.getModifiers(); 1.428 + if ((Modifier.isPrivate(mods)) && 1.429 + (Modifier.isStatic(mods)) && 1.430 + (Modifier.isFinal(mods))) 1.431 + { 1.432 + fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl)); 1.433 + } 1.434 + } catch (NoSuchFieldException e) { 1.435 + fields = null; 1.436 + } catch (IllegalAccessException e) { 1.437 + fields = null; 1.438 + } catch (IllegalArgumentException e) { 1.439 + fields = null; 1.440 + } catch (ClassCastException e) { 1.441 + /* Thrown if a field serialPersistentField exists 1.442 + * but it is not of type ObjectStreamField. 1.443 + */ 1.444 + fields = null; 1.445 + } 1.446 + 1.447 + 1.448 + if (fields == null) { 1.449 + /* Get all of the declared fields for this 1.450 + * Class. setAccessible on all fields so they 1.451 + * can be accessed later. Create a temporary 1.452 + * ObjectStreamField array to hold each 1.453 + * non-static, non-transient field. Then copy the 1.454 + * temporary array into an array of the correct 1.455 + * size once the number of fields is known. 1.456 + */ 1.457 + Field[] actualfields = cl.getDeclaredFields(); 1.458 + 1.459 + int numFields = 0; 1.460 + ObjectStreamField[] tempFields = 1.461 + new ObjectStreamField[actualfields.length]; 1.462 + for (int i = 0; i < actualfields.length; i++) { 1.463 + Field fld = actualfields[i] ; 1.464 + int modifiers = fld.getModifiers(); 1.465 + if (!Modifier.isStatic(modifiers) && 1.466 + !Modifier.isTransient(modifiers)) { 1.467 + fld.setAccessible(true) ; 1.468 + tempFields[numFields++] = new ObjectStreamField(fld); 1.469 + } 1.470 + } 1.471 + 1.472 + fields = new ObjectStreamField[numFields]; 1.473 + System.arraycopy(tempFields, 0, fields, 0, numFields); 1.474 + 1.475 + } else { 1.476 + // For each declared persistent field, look for an actual 1.477 + // reflected Field. If there is one, make sure it's the correct 1.478 + // type and cache it in the ObjectStreamClass for that field. 1.479 + for (int j = fields.length-1; j >= 0; j--) { 1.480 + try { 1.481 + Field reflField = cl.getDeclaredField(fields[j].getName()); 1.482 + if (fields[j].getType() == reflField.getType()) { 1.483 + reflField.setAccessible(true); 1.484 + fields[j].setField(reflField); 1.485 + } 1.486 + } catch (NoSuchFieldException e) { 1.487 + // Nothing to do 1.488 + } 1.489 + } 1.490 + } 1.491 + return null; 1.492 + } 1.493 + }); 1.494 + 1.495 + if (fields.length > 1) 1.496 + Arrays.sort(fields); 1.497 + 1.498 + /* Set up field data for use while writing using the API api. */ 1.499 + computeFieldInfo(); 1.500 + } 1.501 + 1.502 + /* Get the serialVersionUID from the class. 1.503 + * It uses the access override mechanism so make sure 1.504 + * the field objects is only used here. 1.505 + * 1.506 + * NonSerializable classes have a serialVerisonUID of 0L. 1.507 + */ 1.508 + if (isNonSerializable()) { 1.509 + suid = 0L; 1.510 + } else { 1.511 + // Lookup special Serializable members using reflection. 1.512 + AccessController.doPrivileged(new PrivilegedAction() { 1.513 + public Object run() { 1.514 + if (forProxyClass) { 1.515 + // proxy classes always have serialVersionUID of 0L 1.516 + suid = 0L; 1.517 + } else { 1.518 + try { 1.519 + final Field f = cl.getDeclaredField("serialVersionUID"); 1.520 + int mods = f.getModifiers(); 1.521 + // SerialBug 5: static final SUID should be read 1.522 + if (Modifier.isStatic(mods) && Modifier.isFinal(mods) ) { 1.523 + f.setAccessible(true); 1.524 + suid = f.getLong(cl); 1.525 + // SerialBug 2: should be computed after writeObject 1.526 + // actualSuid = computeStructuralUID(cl); 1.527 + } else { 1.528 + suid = _computeSerialVersionUID(cl); 1.529 + // SerialBug 2: should be computed after writeObject 1.530 + // actualSuid = computeStructuralUID(cl); 1.531 + } 1.532 + } catch (NoSuchFieldException ex) { 1.533 + suid = _computeSerialVersionUID(cl); 1.534 + // SerialBug 2: should be computed after writeObject 1.535 + // actualSuid = computeStructuralUID(cl); 1.536 + } catch (IllegalAccessException ex) { 1.537 + suid = _computeSerialVersionUID(cl); 1.538 + } 1.539 + } 1.540 + 1.541 + writeReplaceObjectMethod = ObjectStreamClass.getInheritableMethod(cl, 1.542 + "writeReplace", noTypesList, Object.class); 1.543 + 1.544 + readResolveObjectMethod = ObjectStreamClass.getInheritableMethod(cl, 1.545 + "readResolve", noTypesList, Object.class); 1.546 + 1.547 + if (externalizable) 1.548 + cons = getExternalizableConstructor(cl) ; 1.549 + else 1.550 + cons = getSerializableConstructor(cl) ; 1.551 + 1.552 + if (serializable && !forProxyClass) { 1.553 + /* Look for the writeObject method 1.554 + * Set the accessible flag on it here. ObjectOutputStream 1.555 + * will call it as necessary. 1.556 + */ 1.557 + writeObjectMethod = getPrivateMethod( cl, "writeObject", 1.558 + new Class[] { java.io.ObjectOutputStream.class }, Void.TYPE ) ; 1.559 + readObjectMethod = getPrivateMethod( cl, "readObject", 1.560 + new Class[] { java.io.ObjectInputStream.class }, Void.TYPE ) ; 1.561 + } 1.562 + return null; 1.563 + } 1.564 + }); 1.565 + } 1.566 + 1.567 + // This call depends on a lot of information computed above! 1.568 + actualSuid = ObjectStreamClass.computeStructuralUID(this, cl); 1.569 + 1.570 + // If we have a write object method, precompute the 1.571 + // RMI-IIOP stream format version 2 optional data 1.572 + // repository ID. 1.573 + if (hasWriteObject()) 1.574 + rmiiiopOptionalDataRepId = computeRMIIIOPOptionalDataRepId(); 1.575 + 1.576 + // This must be done last. 1.577 + initialized = true; 1.578 + } 1.579 + } 1.580 + 1.581 + /** 1.582 + * Returns non-static private method with given signature defined by given 1.583 + * class, or null if none found. Access checks are disabled on the 1.584 + * returned method (if any). 1.585 + */ 1.586 + private static Method getPrivateMethod(Class cl, String name, 1.587 + Class[] argTypes, 1.588 + Class returnType) 1.589 + { 1.590 + try { 1.591 + Method meth = cl.getDeclaredMethod(name, argTypes); 1.592 + meth.setAccessible(true); 1.593 + int mods = meth.getModifiers(); 1.594 + return ((meth.getReturnType() == returnType) && 1.595 + ((mods & Modifier.STATIC) == 0) && 1.596 + ((mods & Modifier.PRIVATE) != 0)) ? meth : null; 1.597 + } catch (NoSuchMethodException ex) { 1.598 + return null; 1.599 + } 1.600 + } 1.601 + 1.602 + // Specific to RMI-IIOP 1.603 + /** 1.604 + * Java to IDL ptc-02-01-12 1.5.1 1.605 + * 1.606 + * "The rep_id string passed to the start_value method must be 1.607 + * 'RMI:org.omg.custom.class:hashcode:suid' where class is the 1.608 + * fully-qualified name of the class whose writeObject method 1.609 + * is being invoked and hashcode and suid are the class's hashcode 1.610 + * and SUID." 1.611 + */ 1.612 + private String computeRMIIIOPOptionalDataRepId() { 1.613 + 1.614 + StringBuffer sbuf = new StringBuffer("RMI:org.omg.custom."); 1.615 + sbuf.append(RepositoryId.convertToISOLatin1(this.getName())); 1.616 + sbuf.append(':'); 1.617 + sbuf.append(this.getActualSerialVersionUIDStr()); 1.618 + sbuf.append(':'); 1.619 + sbuf.append(this.getSerialVersionUIDStr()); 1.620 + 1.621 + return sbuf.toString(); 1.622 + } 1.623 + 1.624 + /** 1.625 + * This will return null if there is no writeObject method. 1.626 + */ 1.627 + public final String getRMIIIOPOptionalDataRepId() { 1.628 + return rmiiiopOptionalDataRepId; 1.629 + } 1.630 + 1.631 + /* 1.632 + * Create an empty ObjectStreamClass for a class about to be read. 1.633 + * This is separate from read so ObjectInputStream can assign the 1.634 + * wire handle early, before any nested ObjectStreamClass might 1.635 + * be read. 1.636 + */ 1.637 + ObjectStreamClass(String n, long s) { 1.638 + name = n; 1.639 + suid = s; 1.640 + superclass = null; 1.641 + } 1.642 + 1.643 + private static Object[] translateFields(Object objs[]) 1.644 + throws NoSuchFieldException { 1.645 + try{ 1.646 + java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs; 1.647 + Object translation[] = null; 1.648 + 1.649 + if (translatedFields == null) 1.650 + translatedFields = new Hashtable(); 1.651 + 1.652 + translation = (Object[])translatedFields.get(fields); 1.653 + 1.654 + if (translation != null) 1.655 + return translation; 1.656 + else { 1.657 + Class osfClass = Class.forName("com.sun.corba.se.impl.io.ObjectStreamField"); 1.658 + translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length); 1.659 + Object arg[] = new Object[2]; 1.660 + Class types[] = {String.class, Class.class}; 1.661 + Constructor constructor = osfClass.getDeclaredConstructor(types); 1.662 + for (int i = fields.length -1; i >= 0; i--){ 1.663 + arg[0] = fields[i].getName(); 1.664 + arg[1] = fields[i].getType(); 1.665 + 1.666 + translation[i] = constructor.newInstance(arg); 1.667 + } 1.668 + translatedFields.put(fields, translation); 1.669 + 1.670 + } 1.671 + 1.672 + return (Object[])translation; 1.673 + } 1.674 + catch(Throwable t){ 1.675 + NoSuchFieldException nsfe = new NoSuchFieldException(); 1.676 + nsfe.initCause( t ) ; 1.677 + throw nsfe ; 1.678 + } 1.679 + } 1.680 + 1.681 + /* 1.682 + * Set the class this version descriptor matches. 1.683 + * The base class name and serializable hash must match. 1.684 + * Fill in the reflected Fields that will be used 1.685 + * for reading. 1.686 + */ 1.687 + final void setClass(Class cl) throws InvalidClassException { 1.688 + 1.689 + if (cl == null) { 1.690 + localClassDesc = null; 1.691 + ofClass = null; 1.692 + computeFieldInfo(); 1.693 + return; 1.694 + } 1.695 + 1.696 + localClassDesc = lookupInternal(cl); 1.697 + if (localClassDesc == null) 1.698 + // XXX I18N, logging needed 1.699 + throw new InvalidClassException(cl.getName(), 1.700 + "Local class not compatible"); 1.701 + if (suid != localClassDesc.suid) { 1.702 + 1.703 + /* Check for exceptional cases that allow mismatched suid. */ 1.704 + 1.705 + /* Allow adding Serializable or Externalizable 1.706 + * to a later release of the class. 1.707 + */ 1.708 + boolean addedSerialOrExtern = 1.709 + isNonSerializable() || localClassDesc.isNonSerializable(); 1.710 + 1.711 + /* Disregard the serialVersionUID of an array 1.712 + * when name and cl.Name differ. If resolveClass() returns 1.713 + * an array with a different package name, 1.714 + * the serialVersionUIDs will not match since the fully 1.715 + * qualified array class is used in the 1.716 + * computation of the array's serialVersionUID. There is 1.717 + * no way to set a permanent serialVersionUID for an array type. 1.718 + */ 1.719 + 1.720 + boolean arraySUID = (cl.isArray() && ! cl.getName().equals(name)); 1.721 + 1.722 + if (! arraySUID && ! addedSerialOrExtern ) { 1.723 + // XXX I18N, logging needed 1.724 + throw new InvalidClassException(cl.getName(), 1.725 + "Local class not compatible:" + 1.726 + " stream classdesc serialVersionUID=" + suid + 1.727 + " local class serialVersionUID=" + localClassDesc.suid); 1.728 + } 1.729 + } 1.730 + 1.731 + /* compare the class names, stripping off package names. */ 1.732 + if (! compareClassNames(name, cl.getName(), '.')) 1.733 + // XXX I18N, logging needed 1.734 + throw new InvalidClassException(cl.getName(), 1.735 + "Incompatible local class name. " + 1.736 + "Expected class name compatible with " + 1.737 + name); 1.738 + 1.739 + /* 1.740 + * Test that both implement either serializable or externalizable. 1.741 + */ 1.742 + 1.743 + // The next check is more generic, since it covers the 1.744 + // Proxy case, the JDK 1.3 serialization code has 1.745 + // both checks 1.746 + //if ((serializable && localClassDesc.externalizable) || 1.747 + // (externalizable && localClassDesc.serializable)) 1.748 + // throw new InvalidClassException(localCl.getName(), 1.749 + // "Serializable is incompatible with Externalizable"); 1.750 + 1.751 + if ((serializable != localClassDesc.serializable) || 1.752 + (externalizable != localClassDesc.externalizable) || 1.753 + (!serializable && !externalizable)) 1.754 + 1.755 + // XXX I18N, logging needed 1.756 + throw new InvalidClassException(cl.getName(), 1.757 + "Serialization incompatible with Externalization"); 1.758 + 1.759 + /* Set up the reflected Fields in the class where the value of each 1.760 + * field in this descriptor should be stored. 1.761 + * Each field in this ObjectStreamClass (the source) is located (by 1.762 + * name) in the ObjectStreamClass of the class(the destination). 1.763 + * In the usual (non-versioned case) the field is in both 1.764 + * descriptors and the types match, so the reflected Field is copied. 1.765 + * If the type does not match, a InvalidClass exception is thrown. 1.766 + * If the field is not present in the class, the reflected Field 1.767 + * remains null so the field will be read but discarded. 1.768 + * If extra fields are present in the class they are ignored. Their 1.769 + * values will be set to the default value by the object allocator. 1.770 + * Both the src and dest field list are sorted by type and name. 1.771 + */ 1.772 + 1.773 + ObjectStreamField[] destfield = 1.774 + (ObjectStreamField[])localClassDesc.fields; 1.775 + ObjectStreamField[] srcfield = 1.776 + (ObjectStreamField[])fields; 1.777 + 1.778 + int j = 0; 1.779 + nextsrc: 1.780 + for (int i = 0; i < srcfield.length; i++ ) { 1.781 + /* Find this field in the dest*/ 1.782 + for (int k = j; k < destfield.length; k++) { 1.783 + if (srcfield[i].getName().equals(destfield[k].getName())) { 1.784 + /* found match */ 1.785 + if (srcfield[i].isPrimitive() && 1.786 + !srcfield[i].typeEquals(destfield[k])) { 1.787 + // XXX I18N, logging needed 1.788 + throw new InvalidClassException(cl.getName(), 1.789 + "The type of field " + 1.790 + srcfield[i].getName() + 1.791 + " of class " + name + 1.792 + " is incompatible."); 1.793 + } 1.794 + 1.795 + /* Skip over any fields in the dest that are not in the src */ 1.796 + j = k; 1.797 + 1.798 + srcfield[i].setField(destfield[j].getField()); 1.799 + // go on to the next source field 1.800 + continue nextsrc; 1.801 + } 1.802 + } 1.803 + } 1.804 + 1.805 + /* Set up field data for use while reading from the input stream. */ 1.806 + computeFieldInfo(); 1.807 + 1.808 + /* Remember the class this represents */ 1.809 + ofClass = cl; 1.810 + 1.811 + /* get the cache of these methods from the local class 1.812 + * implementation. 1.813 + */ 1.814 + readObjectMethod = localClassDesc.readObjectMethod; 1.815 + readResolveObjectMethod = localClassDesc.readResolveObjectMethod; 1.816 + } 1.817 + 1.818 + /* Compare the base class names of streamName and localName. 1.819 + * 1.820 + * @return Return true iff the base class name compare. 1.821 + * @parameter streamName Fully qualified class name. 1.822 + * @parameter localName Fully qualified class name. 1.823 + * @parameter pkgSeparator class names use either '.' or '/'. 1.824 + * 1.825 + * Only compare base class name to allow package renaming. 1.826 + */ 1.827 + static boolean compareClassNames(String streamName, 1.828 + String localName, 1.829 + char pkgSeparator) { 1.830 + /* compare the class names, stripping off package names. */ 1.831 + int streamNameIndex = streamName.lastIndexOf(pkgSeparator); 1.832 + if (streamNameIndex < 0) 1.833 + streamNameIndex = 0; 1.834 + 1.835 + int localNameIndex = localName.lastIndexOf(pkgSeparator); 1.836 + if (localNameIndex < 0) 1.837 + localNameIndex = 0; 1.838 + 1.839 + return streamName.regionMatches(false, streamNameIndex, 1.840 + localName, localNameIndex, 1.841 + streamName.length() - streamNameIndex); 1.842 + } 1.843 + 1.844 + /* 1.845 + * Compare the types of two class descriptors. 1.846 + * They match if they have the same class name and suid 1.847 + */ 1.848 + final boolean typeEquals(ObjectStreamClass other) { 1.849 + return (suid == other.suid) && 1.850 + compareClassNames(name, other.name, '.'); 1.851 + } 1.852 + 1.853 + /* 1.854 + * Return the superclass descriptor of this descriptor. 1.855 + */ 1.856 + final void setSuperclass(ObjectStreamClass s) { 1.857 + superclass = s; 1.858 + } 1.859 + 1.860 + /* 1.861 + * Return the superclass descriptor of this descriptor. 1.862 + */ 1.863 + final ObjectStreamClass getSuperclass() { 1.864 + return superclass; 1.865 + } 1.866 + 1.867 + /** 1.868 + * Return whether the class has a readObject method 1.869 + */ 1.870 + final boolean hasReadObject() { 1.871 + return readObjectMethod != null; 1.872 + } 1.873 + 1.874 + /* 1.875 + * Return whether the class has a writeObject method 1.876 + */ 1.877 + final boolean hasWriteObject() { 1.878 + return writeObjectMethod != null ; 1.879 + } 1.880 + 1.881 + /** 1.882 + * Returns when or not this class should be custom 1.883 + * marshaled (use chunking). This should happen if 1.884 + * it is Externalizable OR if it or 1.885 + * any of its superclasses has a writeObject method, 1.886 + */ 1.887 + final boolean isCustomMarshaled() { 1.888 + return (hasWriteObject() || isExternalizable()) 1.889 + || (superclass != null && superclass.isCustomMarshaled()); 1.890 + } 1.891 + 1.892 + /* 1.893 + * Return true if all instances of 'this' Externalizable class 1.894 + * are written in block-data mode from the stream that 'this' was read 1.895 + * from. <p> 1.896 + * 1.897 + * In JDK 1.1, all Externalizable instances are not written 1.898 + * in block-data mode. 1.899 + * In JDK 1.2, all Externalizable instances, by default, are written 1.900 + * in block-data mode and the Externalizable instance is terminated with 1.901 + * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable 1.902 + * instances. 1.903 + * 1.904 + * IMPLEMENTATION NOTE: 1.905 + * This should have been a mode maintained per stream; however, 1.906 + * for compatibility reasons, it was only possible to record 1.907 + * this change per class. All Externalizable classes within 1.908 + * a given stream should either have this mode enabled or 1.909 + * disabled. This is enforced by not allowing the PROTOCOL_VERSION 1.910 + * of a stream to he changed after any objects have been written. 1.911 + * 1.912 + * @see ObjectOutputStream#useProtocolVersion 1.913 + * @see ObjectStreamConstants#PROTOCOL_VERSION_1 1.914 + * @see ObjectStreamConstants#PROTOCOL_VERSION_2 1.915 + * 1.916 + * @since JDK 1.2 1.917 + */ 1.918 + boolean hasExternalizableBlockDataMode() { 1.919 + return hasExternalizableBlockData; 1.920 + } 1.921 + 1.922 + /** 1.923 + * Creates a new instance of the represented class. If the class is 1.924 + * externalizable, invokes its public no-arg constructor; otherwise, if the 1.925 + * class is serializable, invokes the no-arg constructor of the first 1.926 + * non-serializable superclass. Throws UnsupportedOperationException if 1.927 + * this class descriptor is not associated with a class, if the associated 1.928 + * class is non-serializable or if the appropriate no-arg constructor is 1.929 + * inaccessible/unavailable. 1.930 + */ 1.931 + Object newInstance() 1.932 + throws InstantiationException, InvocationTargetException, 1.933 + UnsupportedOperationException 1.934 + { 1.935 + if (cons != null) { 1.936 + try { 1.937 + return cons.newInstance(new Object[0]); 1.938 + } catch (IllegalAccessException ex) { 1.939 + // should not occur, as access checks have been suppressed 1.940 + InternalError ie = new InternalError(); 1.941 + ie.initCause( ex ) ; 1.942 + throw ie ; 1.943 + } 1.944 + } else { 1.945 + throw new UnsupportedOperationException(); 1.946 + } 1.947 + } 1.948 + 1.949 + /** 1.950 + * Returns public no-arg constructor of given class, or null if none found. 1.951 + * Access checks are disabled on the returned constructor (if any), since 1.952 + * the defining class may still be non-public. 1.953 + */ 1.954 + private static Constructor getExternalizableConstructor(Class cl) { 1.955 + try { 1.956 + Constructor cons = cl.getDeclaredConstructor(new Class[0]); 1.957 + cons.setAccessible(true); 1.958 + return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ? 1.959 + cons : null; 1.960 + } catch (NoSuchMethodException ex) { 1.961 + return null; 1.962 + } 1.963 + } 1.964 + 1.965 + /** 1.966 + * Returns subclass-accessible no-arg constructor of first non-serializable 1.967 + * superclass, or null if none found. Access checks are disabled on the 1.968 + * returned constructor (if any). 1.969 + */ 1.970 + private static Constructor getSerializableConstructor(Class cl) { 1.971 + Class initCl = cl; 1.972 + while (Serializable.class.isAssignableFrom(initCl)) { 1.973 + if ((initCl = initCl.getSuperclass()) == null) { 1.974 + return null; 1.975 + } 1.976 + } 1.977 + try { 1.978 + Constructor cons = initCl.getDeclaredConstructor(new Class[0]); 1.979 + int mods = cons.getModifiers(); 1.980 + if ((mods & Modifier.PRIVATE) != 0 || 1.981 + ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 && 1.982 + !packageEquals(cl, initCl))) 1.983 + { 1.984 + return null; 1.985 + } 1.986 + cons = bridge.newConstructorForSerialization(cl, cons); 1.987 + cons.setAccessible(true); 1.988 + return cons; 1.989 + } catch (NoSuchMethodException ex) { 1.990 + return null; 1.991 + } 1.992 + } 1.993 + 1.994 + /* 1.995 + * Return the ObjectStreamClass of the local class this one is based on. 1.996 + */ 1.997 + final ObjectStreamClass localClassDescriptor() { 1.998 + return localClassDesc; 1.999 + } 1.1000 + 1.1001 + /* 1.1002 + * Get the Serializability of the class. 1.1003 + */ 1.1004 + boolean isSerializable() { 1.1005 + return serializable; 1.1006 + } 1.1007 + 1.1008 + /* 1.1009 + * Get the externalizability of the class. 1.1010 + */ 1.1011 + boolean isExternalizable() { 1.1012 + return externalizable; 1.1013 + } 1.1014 + 1.1015 + boolean isNonSerializable() { 1.1016 + return ! (externalizable || serializable); 1.1017 + } 1.1018 + 1.1019 + /* 1.1020 + * Calculate the size of the array needed to store primitive data and the 1.1021 + * number of object references to read when reading from the input 1.1022 + * stream. 1.1023 + */ 1.1024 + private void computeFieldInfo() { 1.1025 + primBytes = 0; 1.1026 + objFields = 0; 1.1027 + 1.1028 + for (int i = 0; i < fields.length; i++ ) { 1.1029 + switch (fields[i].getTypeCode()) { 1.1030 + case 'B': 1.1031 + case 'Z': 1.1032 + primBytes += 1; 1.1033 + break; 1.1034 + case 'C': 1.1035 + case 'S': 1.1036 + primBytes += 2; 1.1037 + break; 1.1038 + 1.1039 + case 'I': 1.1040 + case 'F': 1.1041 + primBytes += 4; 1.1042 + break; 1.1043 + case 'J': 1.1044 + case 'D' : 1.1045 + primBytes += 8; 1.1046 + break; 1.1047 + 1.1048 + case 'L': 1.1049 + case '[': 1.1050 + objFields += 1; 1.1051 + break; 1.1052 + } 1.1053 + } 1.1054 + } 1.1055 + 1.1056 + private static void msg( String str ) 1.1057 + { 1.1058 + System.out.println( str ) ; 1.1059 + } 1.1060 + 1.1061 + /* JDK 1.5 has introduced some new modifier bits (such as SYNTHETIC) 1.1062 + * that can affect the SVUID computation (see bug 4897937). These bits 1.1063 + * must be ignored, as otherwise interoperability with ORBs in earlier 1.1064 + * JDK versions can be compromised. I am adding these masks for this 1.1065 + * purpose as discussed in the CCC for this bug (see http://ccc.sfbay/4897937). 1.1066 + */ 1.1067 + 1.1068 + public static final int CLASS_MASK = Modifier.PUBLIC | Modifier.FINAL | 1.1069 + Modifier.INTERFACE | Modifier.ABSTRACT ; 1.1070 + public static final int FIELD_MASK = Modifier.PUBLIC | Modifier.PRIVATE | 1.1071 + Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | 1.1072 + Modifier.TRANSIENT | Modifier.VOLATILE ; 1.1073 + public static final int METHOD_MASK = Modifier.PUBLIC | Modifier.PRIVATE | 1.1074 + Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | 1.1075 + Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT | 1.1076 + Modifier.STRICT ; 1.1077 + 1.1078 + /* 1.1079 + * Compute a hash for the specified class. Incrementally add 1.1080 + * items to the hash accumulating in the digest stream. 1.1081 + * Fold the hash into a long. Use the SHA secure hash function. 1.1082 + */ 1.1083 + private static long _computeSerialVersionUID(Class cl) { 1.1084 + if (DEBUG_SVUID) 1.1085 + msg( "Computing SerialVersionUID for " + cl ) ; 1.1086 + ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); 1.1087 + 1.1088 + long h = 0; 1.1089 + try { 1.1090 + MessageDigest md = MessageDigest.getInstance("SHA"); 1.1091 + DigestOutputStream mdo = new DigestOutputStream(devnull, md); 1.1092 + DataOutputStream data = new DataOutputStream(mdo); 1.1093 + 1.1094 + if (DEBUG_SVUID) 1.1095 + msg( "\twriteUTF( \"" + cl.getName() + "\" )" ) ; 1.1096 + data.writeUTF(cl.getName()); 1.1097 + 1.1098 + int classaccess = cl.getModifiers(); 1.1099 + classaccess &= (Modifier.PUBLIC | Modifier.FINAL | 1.1100 + Modifier.INTERFACE | Modifier.ABSTRACT); 1.1101 + 1.1102 + /* Workaround for javac bug that only set ABSTRACT for 1.1103 + * interfaces if the interface had some methods. 1.1104 + * The ABSTRACT bit reflects that the number of methods > 0. 1.1105 + * This is required so correct hashes can be computed 1.1106 + * for existing class files. 1.1107 + * Previously this hack was previously present in the VM. 1.1108 + */ 1.1109 + Method[] method = cl.getDeclaredMethods(); 1.1110 + if ((classaccess & Modifier.INTERFACE) != 0) { 1.1111 + classaccess &= (~Modifier.ABSTRACT); 1.1112 + if (method.length > 0) { 1.1113 + classaccess |= Modifier.ABSTRACT; 1.1114 + } 1.1115 + } 1.1116 + 1.1117 + // Mask out any post-1.4 attributes 1.1118 + classaccess &= CLASS_MASK ; 1.1119 + 1.1120 + if (DEBUG_SVUID) 1.1121 + msg( "\twriteInt( " + classaccess + " ) " ) ; 1.1122 + data.writeInt(classaccess); 1.1123 + 1.1124 + /* 1.1125 + * Get the list of interfaces supported, 1.1126 + * Accumulate their names their names in Lexical order 1.1127 + * and add them to the hash 1.1128 + */ 1.1129 + if (!cl.isArray()) { 1.1130 + /* In 1.2fcs, getInterfaces() was modified to return 1.1131 + * {java.lang.Cloneable, java.io.Serializable} when 1.1132 + * called on array classes. These values would upset 1.1133 + * the computation of the hash, so we explicitly omit 1.1134 + * them from its computation. 1.1135 + */ 1.1136 + 1.1137 + Class interfaces[] = cl.getInterfaces(); 1.1138 + Arrays.sort(interfaces, compareClassByName); 1.1139 + 1.1140 + for (int i = 0; i < interfaces.length; i++) { 1.1141 + if (DEBUG_SVUID) 1.1142 + msg( "\twriteUTF( \"" + interfaces[i].getName() + "\" ) " ) ; 1.1143 + data.writeUTF(interfaces[i].getName()); 1.1144 + } 1.1145 + } 1.1146 + 1.1147 + /* Sort the field names to get a deterministic order */ 1.1148 + Field[] field = cl.getDeclaredFields(); 1.1149 + Arrays.sort(field, compareMemberByName); 1.1150 + 1.1151 + for (int i = 0; i < field.length; i++) { 1.1152 + Field f = field[i]; 1.1153 + 1.1154 + /* Include in the hash all fields except those that are 1.1155 + * private transient and private static. 1.1156 + */ 1.1157 + int m = f.getModifiers(); 1.1158 + if (Modifier.isPrivate(m) && 1.1159 + (Modifier.isTransient(m) || Modifier.isStatic(m))) 1.1160 + continue; 1.1161 + 1.1162 + if (DEBUG_SVUID) 1.1163 + msg( "\twriteUTF( \"" + f.getName() + "\" ) " ) ; 1.1164 + data.writeUTF(f.getName()); 1.1165 + 1.1166 + // Mask out any post-1.4 bits 1.1167 + m &= FIELD_MASK ; 1.1168 + 1.1169 + if (DEBUG_SVUID) 1.1170 + msg( "\twriteInt( " + m + " ) " ) ; 1.1171 + data.writeInt(m); 1.1172 + 1.1173 + if (DEBUG_SVUID) 1.1174 + msg( "\twriteUTF( \"" + getSignature(f.getType()) + "\" ) " ) ; 1.1175 + data.writeUTF(getSignature(f.getType())); 1.1176 + } 1.1177 + 1.1178 + if (hasStaticInitializer(cl)) { 1.1179 + if (DEBUG_SVUID) 1.1180 + msg( "\twriteUTF( \"<clinit>\" ) " ) ; 1.1181 + data.writeUTF("<clinit>"); 1.1182 + 1.1183 + if (DEBUG_SVUID) 1.1184 + msg( "\twriteInt( " + Modifier.STATIC + " )" ) ; 1.1185 + data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have 1.1186 + 1.1187 + if (DEBUG_SVUID) 1.1188 + msg( "\twriteUTF( \"()V\" )" ) ; 1.1189 + data.writeUTF("()V"); 1.1190 + } 1.1191 + 1.1192 + /* 1.1193 + * Get the list of constructors including name and signature 1.1194 + * Sort lexically, add all except the private constructors 1.1195 + * to the hash with their access flags 1.1196 + */ 1.1197 + 1.1198 + MethodSignature[] constructors = 1.1199 + MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors()); 1.1200 + for (int i = 0; i < constructors.length; i++) { 1.1201 + MethodSignature c = constructors[i]; 1.1202 + String mname = "<init>"; 1.1203 + String desc = c.signature; 1.1204 + desc = desc.replace('/', '.'); 1.1205 + if (DEBUG_SVUID) 1.1206 + msg( "\twriteUTF( \"" + mname + "\" )" ) ; 1.1207 + data.writeUTF(mname); 1.1208 + 1.1209 + // mask out post-1.4 modifiers 1.1210 + int modifier = c.member.getModifiers() & METHOD_MASK ; 1.1211 + 1.1212 + if (DEBUG_SVUID) 1.1213 + msg( "\twriteInt( " + modifier + " ) " ) ; 1.1214 + data.writeInt( modifier ) ; 1.1215 + 1.1216 + if (DEBUG_SVUID) 1.1217 + msg( "\twriteUTF( \"" + desc+ "\" )" ) ; 1.1218 + data.writeUTF(desc); 1.1219 + } 1.1220 + 1.1221 + /* Include in the hash all methods except those that are 1.1222 + * private transient and private static. 1.1223 + */ 1.1224 + MethodSignature[] methods = 1.1225 + MethodSignature.removePrivateAndSort(method); 1.1226 + for (int i = 0; i < methods.length; i++ ) { 1.1227 + MethodSignature m = methods[i]; 1.1228 + String desc = m.signature; 1.1229 + desc = desc.replace('/', '.'); 1.1230 + 1.1231 + if (DEBUG_SVUID) 1.1232 + msg( "\twriteUTF( \"" + m.member.getName()+ "\" )" ) ; 1.1233 + data.writeUTF(m.member.getName()); 1.1234 + 1.1235 + // mask out post-1.4 modifiers 1.1236 + int modifier = m.member.getModifiers() & METHOD_MASK ; 1.1237 + 1.1238 + if (DEBUG_SVUID) 1.1239 + msg( "\twriteInt( " + modifier + " ) " ) ; 1.1240 + data.writeInt( modifier ) ; 1.1241 + 1.1242 + if (DEBUG_SVUID) 1.1243 + msg( "\twriteUTF( \"" + desc + "\" )" ) ; 1.1244 + data.writeUTF(desc); 1.1245 + } 1.1246 + 1.1247 + /* Compute the hash value for this class. 1.1248 + * Use only the first 64 bits of the hash. 1.1249 + */ 1.1250 + data.flush(); 1.1251 + byte hasharray[] = md.digest(); 1.1252 + for (int i = 0; i < Math.min(8, hasharray.length); i++) { 1.1253 + h += (long)(hasharray[i] & 255) << (i * 8); 1.1254 + } 1.1255 + } catch (IOException ignore) { 1.1256 + /* can't happen, but be deterministic anyway. */ 1.1257 + h = -1; 1.1258 + } catch (NoSuchAlgorithmException complain) { 1.1259 + SecurityException se = new SecurityException() ; 1.1260 + se.initCause( complain ) ; 1.1261 + throw se ; 1.1262 + } 1.1263 + 1.1264 + return h; 1.1265 + } 1.1266 + 1.1267 + private static long computeStructuralUID(com.sun.corba.se.impl.io.ObjectStreamClass osc, Class cl) { 1.1268 + ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); 1.1269 + 1.1270 + long h = 0; 1.1271 + try { 1.1272 + 1.1273 + if ((!java.io.Serializable.class.isAssignableFrom(cl)) || 1.1274 + (cl.isInterface())){ 1.1275 + return 0; 1.1276 + } 1.1277 + 1.1278 + if (java.io.Externalizable.class.isAssignableFrom(cl)) { 1.1279 + return 1; 1.1280 + } 1.1281 + 1.1282 + MessageDigest md = MessageDigest.getInstance("SHA"); 1.1283 + DigestOutputStream mdo = new DigestOutputStream(devnull, md); 1.1284 + DataOutputStream data = new DataOutputStream(mdo); 1.1285 + 1.1286 + // Get SUID of parent 1.1287 + Class parent = cl.getSuperclass(); 1.1288 + if ((parent != null)) 1.1289 + // SerialBug 1; acc. to spec the one for 1.1290 + // java.lang.object 1.1291 + // should be computed and put 1.1292 + // && (parent != java.lang.Object.class)) 1.1293 + { 1.1294 + //data.writeLong(computeSerialVersionUID(null,parent)); 1.1295 + data.writeLong(computeStructuralUID(lookup(parent), parent)); 1.1296 + } 1.1297 + 1.1298 + if (osc.hasWriteObject()) 1.1299 + data.writeInt(2); 1.1300 + else 1.1301 + data.writeInt(1); 1.1302 + 1.1303 + // CORBA formal 00-11-03 10.6.2: For each field of the 1.1304 + // class that is mapped to IDL, sorted lexicographically 1.1305 + // by Java field name, in increasing order... 1.1306 + ObjectStreamField[] field = osc.getFields(); 1.1307 + if (field.length > 1) { 1.1308 + Arrays.sort(field, compareObjStrFieldsByName); 1.1309 + } 1.1310 + 1.1311 + // ...Java field name in UTF encoding, field 1.1312 + // descriptor, as defined by the JVM spec... 1.1313 + for (int i = 0; i < field.length; i++) { 1.1314 + data.writeUTF(field[i].getName()); 1.1315 + data.writeUTF(field[i].getSignature()); 1.1316 + } 1.1317 + 1.1318 + /* Compute the hash value for this class. 1.1319 + * Use only the first 64 bits of the hash. 1.1320 + */ 1.1321 + data.flush(); 1.1322 + byte hasharray[] = md.digest(); 1.1323 + // int minimum = Math.min(8, hasharray.length); 1.1324 + // SerialBug 3: SHA computation is wrong; for loop reversed 1.1325 + //for (int i = minimum; i > 0; i--) 1.1326 + for (int i = 0; i < Math.min(8, hasharray.length); i++) { 1.1327 + h += (long)(hasharray[i] & 255) << (i * 8); 1.1328 + } 1.1329 + } catch (IOException ignore) { 1.1330 + /* can't happen, but be deterministic anyway. */ 1.1331 + h = -1; 1.1332 + } catch (NoSuchAlgorithmException complain) { 1.1333 + SecurityException se = new SecurityException(); 1.1334 + se.initCause( complain ) ; 1.1335 + throw se ; 1.1336 + } 1.1337 + return h; 1.1338 + } 1.1339 + 1.1340 + /** 1.1341 + * Compute the JVM signature for the class. 1.1342 + */ 1.1343 + static String getSignature(Class clazz) { 1.1344 + String type = null; 1.1345 + if (clazz.isArray()) { 1.1346 + Class cl = clazz; 1.1347 + int dimensions = 0; 1.1348 + while (cl.isArray()) { 1.1349 + dimensions++; 1.1350 + cl = cl.getComponentType(); 1.1351 + } 1.1352 + StringBuffer sb = new StringBuffer(); 1.1353 + for (int i = 0; i < dimensions; i++) { 1.1354 + sb.append("["); 1.1355 + } 1.1356 + sb.append(getSignature(cl)); 1.1357 + type = sb.toString(); 1.1358 + } else if (clazz.isPrimitive()) { 1.1359 + if (clazz == Integer.TYPE) { 1.1360 + type = "I"; 1.1361 + } else if (clazz == Byte.TYPE) { 1.1362 + type = "B"; 1.1363 + } else if (clazz == Long.TYPE) { 1.1364 + type = "J"; 1.1365 + } else if (clazz == Float.TYPE) { 1.1366 + type = "F"; 1.1367 + } else if (clazz == Double.TYPE) { 1.1368 + type = "D"; 1.1369 + } else if (clazz == Short.TYPE) { 1.1370 + type = "S"; 1.1371 + } else if (clazz == Character.TYPE) { 1.1372 + type = "C"; 1.1373 + } else if (clazz == Boolean.TYPE) { 1.1374 + type = "Z"; 1.1375 + } else if (clazz == Void.TYPE) { 1.1376 + type = "V"; 1.1377 + } 1.1378 + } else { 1.1379 + type = "L" + clazz.getName().replace('.', '/') + ";"; 1.1380 + } 1.1381 + return type; 1.1382 + } 1.1383 + 1.1384 + /* 1.1385 + * Compute the JVM method descriptor for the method. 1.1386 + */ 1.1387 + static String getSignature(Method meth) { 1.1388 + StringBuffer sb = new StringBuffer(); 1.1389 + 1.1390 + sb.append("("); 1.1391 + 1.1392 + Class[] params = meth.getParameterTypes(); // avoid clone 1.1393 + for (int j = 0; j < params.length; j++) { 1.1394 + sb.append(getSignature(params[j])); 1.1395 + } 1.1396 + sb.append(")"); 1.1397 + sb.append(getSignature(meth.getReturnType())); 1.1398 + return sb.toString(); 1.1399 + } 1.1400 + 1.1401 + /* 1.1402 + * Compute the JVM constructor descriptor for the constructor. 1.1403 + */ 1.1404 + static String getSignature(Constructor cons) { 1.1405 + StringBuffer sb = new StringBuffer(); 1.1406 + 1.1407 + sb.append("("); 1.1408 + 1.1409 + Class[] params = cons.getParameterTypes(); // avoid clone 1.1410 + for (int j = 0; j < params.length; j++) { 1.1411 + sb.append(getSignature(params[j])); 1.1412 + } 1.1413 + sb.append(")V"); 1.1414 + return sb.toString(); 1.1415 + } 1.1416 + 1.1417 + /* 1.1418 + * Cache of Class -> ClassDescriptor Mappings. 1.1419 + */ 1.1420 + static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61]; 1.1421 + 1.1422 + /* 1.1423 + * findDescriptorFor a Class. This looks in the cache for a 1.1424 + * mapping from Class -> ObjectStreamClass mappings. The hashCode 1.1425 + * of the Class is used for the lookup since the Class is the key. 1.1426 + * The entries are extended from java.lang.ref.SoftReference so the 1.1427 + * gc will be able to free them if needed. 1.1428 + */ 1.1429 + private static ObjectStreamClass findDescriptorFor(Class cl) { 1.1430 + 1.1431 + int hash = cl.hashCode(); 1.1432 + int index = (hash & 0x7FFFFFFF) % descriptorFor.length; 1.1433 + ObjectStreamClassEntry e; 1.1434 + ObjectStreamClassEntry prev; 1.1435 + 1.1436 + /* Free any initial entries whose refs have been cleared */ 1.1437 + while ((e = descriptorFor[index]) != null && e.get() == null) { 1.1438 + descriptorFor[index] = e.next; 1.1439 + } 1.1440 + 1.1441 + /* Traverse the chain looking for a descriptor with ofClass == cl. 1.1442 + * unlink entries that are unresolved. 1.1443 + */ 1.1444 + prev = e; 1.1445 + while (e != null ) { 1.1446 + ObjectStreamClass desc = (ObjectStreamClass)(e.get()); 1.1447 + if (desc == null) { 1.1448 + // This entry has been cleared, unlink it 1.1449 + prev.next = e.next; 1.1450 + } else { 1.1451 + if (desc.ofClass == cl) 1.1452 + return desc; 1.1453 + prev = e; 1.1454 + } 1.1455 + e = e.next; 1.1456 + } 1.1457 + return null; 1.1458 + } 1.1459 + 1.1460 + /* 1.1461 + * insertDescriptorFor a Class -> ObjectStreamClass mapping. 1.1462 + */ 1.1463 + private static void insertDescriptorFor(ObjectStreamClass desc) { 1.1464 + // Make sure not already present 1.1465 + if (findDescriptorFor(desc.ofClass) != null) { 1.1466 + return; 1.1467 + } 1.1468 + 1.1469 + int hash = desc.ofClass.hashCode(); 1.1470 + int index = (hash & 0x7FFFFFFF) % descriptorFor.length; 1.1471 + ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc); 1.1472 + e.next = descriptorFor[index]; 1.1473 + descriptorFor[index] = e; 1.1474 + } 1.1475 + 1.1476 + private static Field[] getDeclaredFields(final Class clz) { 1.1477 + return (Field[]) AccessController.doPrivileged(new PrivilegedAction() { 1.1478 + public Object run() { 1.1479 + return clz.getDeclaredFields(); 1.1480 + } 1.1481 + }); 1.1482 + } 1.1483 + 1.1484 + 1.1485 + /* 1.1486 + * The name of this descriptor 1.1487 + */ 1.1488 + private String name; 1.1489 + 1.1490 + /* 1.1491 + * The descriptor of the supertype. 1.1492 + */ 1.1493 + private ObjectStreamClass superclass; 1.1494 + 1.1495 + /* 1.1496 + * Flags for Serializable and Externalizable. 1.1497 + */ 1.1498 + private boolean serializable; 1.1499 + private boolean externalizable; 1.1500 + 1.1501 + /* 1.1502 + * Array of persistent fields of this class, sorted by 1.1503 + * type and name. 1.1504 + */ 1.1505 + private ObjectStreamField[] fields; 1.1506 + 1.1507 + /* 1.1508 + * Class that is a descriptor for in this virtual machine. 1.1509 + */ 1.1510 + private Class ofClass; 1.1511 + 1.1512 + /* 1.1513 + * True if descriptor for a proxy class. 1.1514 + */ 1.1515 + boolean forProxyClass; 1.1516 + 1.1517 + 1.1518 + /* 1.1519 + * SerialVersionUID for this class. 1.1520 + */ 1.1521 + private long suid = kDefaultUID; 1.1522 + private String suidStr = null; 1.1523 + 1.1524 + /* 1.1525 + * Actual (computed) SerialVersionUID for this class. 1.1526 + */ 1.1527 + private long actualSuid = kDefaultUID; 1.1528 + private String actualSuidStr = null; 1.1529 + 1.1530 + /* 1.1531 + * The total number of bytes of primitive fields. 1.1532 + * The total number of object fields. 1.1533 + */ 1.1534 + int primBytes; 1.1535 + int objFields; 1.1536 + 1.1537 + /** 1.1538 + * Flag indicating whether or not this instance has 1.1539 + * successfully completed initialization. This is to 1.1540 + * try to fix bug 4373844. Working to move to 1.1541 + * reusing java.io.ObjectStreamClass for JDK 1.5. 1.1542 + */ 1.1543 + private boolean initialized = false; 1.1544 + 1.1545 + /* Internal lock object. */ 1.1546 + private Object lock = new Object(); 1.1547 + 1.1548 + /* In JDK 1.1, external data was not written in block mode. 1.1549 + * As of JDK 1.2, external data is written in block data mode. This 1.1550 + * flag enables JDK 1.2 to be able to read JDK 1.1 written external data. 1.1551 + * 1.1552 + * @since JDK 1.2 1.1553 + */ 1.1554 + private boolean hasExternalizableBlockData; 1.1555 + Method writeObjectMethod; 1.1556 + Method readObjectMethod; 1.1557 + private Method writeReplaceObjectMethod; 1.1558 + private Method readResolveObjectMethod; 1.1559 + private Constructor cons ; 1.1560 + 1.1561 + /** 1.1562 + * Beginning in Java to IDL ptc/02-01-12, RMI-IIOP has a 1.1563 + * stream format version 2 which puts a fake valuetype around 1.1564 + * a Serializable's optional custom data. This valuetype has 1.1565 + * a special repository ID made from the Serializable's 1.1566 + * information which we are pre-computing and 1.1567 + * storing here. 1.1568 + */ 1.1569 + private String rmiiiopOptionalDataRepId = null; 1.1570 + 1.1571 + /* 1.1572 + * ObjectStreamClass that this one was built from. 1.1573 + */ 1.1574 + private ObjectStreamClass localClassDesc; 1.1575 + 1.1576 + /* Find out if the class has a static class initializer <clinit> */ 1.1577 + private static Method hasStaticInitializerMethod = null; 1.1578 + /** 1.1579 + * Returns true if the given class defines a static initializer method, 1.1580 + * false otherwise. 1.1581 + */ 1.1582 + private static boolean hasStaticInitializer(Class cl) { 1.1583 + if (hasStaticInitializerMethod == null) { 1.1584 + Class classWithThisMethod = null; 1.1585 + 1.1586 + try { 1.1587 + try { 1.1588 + // When using rip-int with Merlin or when this is a Merlin 1.1589 + // workspace, the method we want is in sun.misc.ClassReflector 1.1590 + // and absent from java.io.ObjectStreamClass. 1.1591 + // 1.1592 + // When compiling rip-int with JDK 1.3.x, we have to get it 1.1593 + // from java.io.ObjectStreamClass. 1.1594 + classWithThisMethod = Class.forName("sun.misc.ClassReflector"); 1.1595 + } catch (ClassNotFoundException cnfe) { 1.1596 + // Do nothing. This is either not a Merlin workspace, 1.1597 + // or rip-int is being compiled with something other than 1.1598 + // Merlin, probably JDK 1.3. Fall back on java.io.ObjectStreaClass. 1.1599 + } 1.1600 + if (classWithThisMethod == null) 1.1601 + classWithThisMethod = java.io.ObjectStreamClass.class; 1.1602 + 1.1603 + hasStaticInitializerMethod = 1.1604 + classWithThisMethod.getDeclaredMethod("hasStaticInitializer", 1.1605 + new Class[] { Class.class }); 1.1606 + } catch (NoSuchMethodException ex) { 1.1607 + } 1.1608 + 1.1609 + if (hasStaticInitializerMethod == null) { 1.1610 + // XXX I18N, logging needed 1.1611 + throw new InternalError("Can't find hasStaticInitializer method on " 1.1612 + + classWithThisMethod.getName()); 1.1613 + } 1.1614 + hasStaticInitializerMethod.setAccessible(true); 1.1615 + } 1.1616 + 1.1617 + try { 1.1618 + Boolean retval = (Boolean) 1.1619 + hasStaticInitializerMethod.invoke(null, new Object[] { cl }); 1.1620 + return retval.booleanValue(); 1.1621 + } catch (Exception ex) { 1.1622 + // XXX I18N, logging needed 1.1623 + InternalError ie = new InternalError( "Error invoking hasStaticInitializer" ) ; 1.1624 + ie.initCause( ex ) ; 1.1625 + throw ie ; 1.1626 + } 1.1627 + } 1.1628 + 1.1629 + 1.1630 + /* The Class Object for java.io.Serializable */ 1.1631 + private static Class classSerializable = null; 1.1632 + private static Class classExternalizable = null; 1.1633 + 1.1634 + /* 1.1635 + * Resolve java.io.Serializable at load time. 1.1636 + */ 1.1637 + static { 1.1638 + try { 1.1639 + classSerializable = Class.forName("java.io.Serializable"); 1.1640 + classExternalizable = Class.forName("java.io.Externalizable"); 1.1641 + } catch (Throwable e) { 1.1642 + System.err.println("Could not load java.io.Serializable or java.io.Externalizable."); 1.1643 + } 1.1644 + } 1.1645 + 1.1646 + /** use serialVersionUID from JDK 1.1. for interoperability */ 1.1647 + private static final long serialVersionUID = -6120832682080437368L; 1.1648 + 1.1649 + /** 1.1650 + * Set serialPersistentFields of a Serializable class to this value to 1.1651 + * denote that the class has no Serializable fields. 1.1652 + */ 1.1653 + public static final ObjectStreamField[] NO_FIELDS = 1.1654 + new ObjectStreamField[0]; 1.1655 + 1.1656 + /* 1.1657 + * Entries held in the Cache of known ObjectStreamClass objects. 1.1658 + * Entries are chained together with the same hash value (modulo array size). 1.1659 + */ 1.1660 + private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference 1.1661 + { 1.1662 + ObjectStreamClassEntry(ObjectStreamClass c) { 1.1663 + //super(c); 1.1664 + this.c = c; 1.1665 + } 1.1666 + ObjectStreamClassEntry next; 1.1667 + 1.1668 + public Object get() 1.1669 + { 1.1670 + return c; 1.1671 + } 1.1672 + private ObjectStreamClass c; 1.1673 + } 1.1674 + 1.1675 + /* 1.1676 + * Comparator object for Classes and Interfaces 1.1677 + */ 1.1678 + private static Comparator compareClassByName = 1.1679 + new CompareClassByName(); 1.1680 + 1.1681 + private static class CompareClassByName implements Comparator { 1.1682 + public int compare(Object o1, Object o2) { 1.1683 + Class c1 = (Class)o1; 1.1684 + Class c2 = (Class)o2; 1.1685 + return (c1.getName()).compareTo(c2.getName()); 1.1686 + } 1.1687 + } 1.1688 + 1.1689 + /** 1.1690 + * Comparator for ObjectStreamFields by name 1.1691 + */ 1.1692 + private final static Comparator compareObjStrFieldsByName 1.1693 + = new CompareObjStrFieldsByName(); 1.1694 + 1.1695 + private static class CompareObjStrFieldsByName implements Comparator { 1.1696 + public int compare(Object o1, Object o2) { 1.1697 + ObjectStreamField osf1 = (ObjectStreamField)o1; 1.1698 + ObjectStreamField osf2 = (ObjectStreamField)o2; 1.1699 + 1.1700 + return osf1.getName().compareTo(osf2.getName()); 1.1701 + } 1.1702 + } 1.1703 + 1.1704 + /* 1.1705 + * Comparator object for Members, Fields, and Methods 1.1706 + */ 1.1707 + private static Comparator compareMemberByName = 1.1708 + new CompareMemberByName(); 1.1709 + 1.1710 + private static class CompareMemberByName implements Comparator { 1.1711 + public int compare(Object o1, Object o2) { 1.1712 + String s1 = ((Member)o1).getName(); 1.1713 + String s2 = ((Member)o2).getName(); 1.1714 + 1.1715 + if (o1 instanceof Method) { 1.1716 + s1 += getSignature((Method)o1); 1.1717 + s2 += getSignature((Method)o2); 1.1718 + } else if (o1 instanceof Constructor) { 1.1719 + s1 += getSignature((Constructor)o1); 1.1720 + s2 += getSignature((Constructor)o2); 1.1721 + } 1.1722 + return s1.compareTo(s2); 1.1723 + } 1.1724 + } 1.1725 + 1.1726 + /* It is expensive to recompute a method or constructor signature 1.1727 + many times, so compute it only once using this data structure. */ 1.1728 + private static class MethodSignature implements Comparator { 1.1729 + Member member; 1.1730 + String signature; // cached parameter signature 1.1731 + 1.1732 + /* Given an array of Method or Constructor members, 1.1733 + return a sorted array of the non-private members.*/ 1.1734 + /* A better implementation would be to implement the returned data 1.1735 + structure as an insertion sorted link list.*/ 1.1736 + static MethodSignature[] removePrivateAndSort(Member[] m) { 1.1737 + int numNonPrivate = 0; 1.1738 + for (int i = 0; i < m.length; i++) { 1.1739 + if (! Modifier.isPrivate(m[i].getModifiers())) { 1.1740 + numNonPrivate++; 1.1741 + } 1.1742 + } 1.1743 + MethodSignature[] cm = new MethodSignature[numNonPrivate]; 1.1744 + int cmi = 0; 1.1745 + for (int i = 0; i < m.length; i++) { 1.1746 + if (! Modifier.isPrivate(m[i].getModifiers())) { 1.1747 + cm[cmi] = new MethodSignature(m[i]); 1.1748 + cmi++; 1.1749 + } 1.1750 + } 1.1751 + if (cmi > 0) 1.1752 + Arrays.sort(cm, cm[0]); 1.1753 + return cm; 1.1754 + } 1.1755 + 1.1756 + /* Assumes that o1 and o2 are either both methods 1.1757 + or both constructors.*/ 1.1758 + public int compare(Object o1, Object o2) { 1.1759 + /* Arrays.sort calls compare when o1 and o2 are equal.*/ 1.1760 + if (o1 == o2) 1.1761 + return 0; 1.1762 + 1.1763 + MethodSignature c1 = (MethodSignature)o1; 1.1764 + MethodSignature c2 = (MethodSignature)o2; 1.1765 + 1.1766 + int result; 1.1767 + if (isConstructor()) { 1.1768 + result = c1.signature.compareTo(c2.signature); 1.1769 + } else { // is a Method. 1.1770 + result = c1.member.getName().compareTo(c2.member.getName()); 1.1771 + if (result == 0) 1.1772 + result = c1.signature.compareTo(c2.signature); 1.1773 + } 1.1774 + return result; 1.1775 + } 1.1776 + 1.1777 + final private boolean isConstructor() { 1.1778 + return member instanceof Constructor; 1.1779 + } 1.1780 + private MethodSignature(Member m) { 1.1781 + member = m; 1.1782 + if (isConstructor()) { 1.1783 + signature = ObjectStreamClass.getSignature((Constructor)m); 1.1784 + } else { 1.1785 + signature = ObjectStreamClass.getSignature((Method)m); 1.1786 + } 1.1787 + } 1.1788 + } 1.1789 + 1.1790 + /** 1.1791 + * Returns non-static, non-abstract method with given signature provided it 1.1792 + * is defined by or accessible (via inheritance) by the given class, or 1.1793 + * null if no match found. Access checks are disabled on the returned 1.1794 + * method (if any). 1.1795 + * 1.1796 + * Copied from the Merlin java.io.ObjectStreamClass. 1.1797 + */ 1.1798 + private static Method getInheritableMethod(Class cl, String name, 1.1799 + Class[] argTypes, 1.1800 + Class returnType) 1.1801 + { 1.1802 + Method meth = null; 1.1803 + Class defCl = cl; 1.1804 + while (defCl != null) { 1.1805 + try { 1.1806 + meth = defCl.getDeclaredMethod(name, argTypes); 1.1807 + break; 1.1808 + } catch (NoSuchMethodException ex) { 1.1809 + defCl = defCl.getSuperclass(); 1.1810 + } 1.1811 + } 1.1812 + 1.1813 + if ((meth == null) || (meth.getReturnType() != returnType)) { 1.1814 + return null; 1.1815 + } 1.1816 + meth.setAccessible(true); 1.1817 + int mods = meth.getModifiers(); 1.1818 + if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) { 1.1819 + return null; 1.1820 + } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) { 1.1821 + return meth; 1.1822 + } else if ((mods & Modifier.PRIVATE) != 0) { 1.1823 + return (cl == defCl) ? meth : null; 1.1824 + } else { 1.1825 + return packageEquals(cl, defCl) ? meth : null; 1.1826 + } 1.1827 + } 1.1828 + 1.1829 + /** 1.1830 + * Returns true if classes are defined in the same package, false 1.1831 + * otherwise. 1.1832 + * 1.1833 + * Copied from the Merlin java.io.ObjectStreamClass. 1.1834 + */ 1.1835 + private static boolean packageEquals(Class cl1, Class cl2) { 1.1836 + Package pkg1 = cl1.getPackage(), pkg2 = cl2.getPackage(); 1.1837 + return ((pkg1 == pkg2) || ((pkg1 != null) && (pkg1.equals(pkg2)))); 1.1838 + } 1.1839 +}