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

changeset 1
55540e827aef
child 68
4906dae0c5fa
     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 +}

mercurial