classpath
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Serialization bug fixes


From: Andrew Haley
Subject: Serialization bug fixes
Date: Thu, 17 Jun 2004 15:59:14 +0100

I found some serialization bugs when debugging JOnAS.  The fixes are
on gcj-abi-2-dev-branch for some while, but I've kept them to myself
for too long.

The current implementation doesn't handle nested objects very well,
and doesn't use the correct class loader.  I fixed that, and a few
minor things as well.

The good news is that with these fixes, serialization seems to work
well, even in a very complex app.

The bad news is that I've added a ton of extra debugging which
obfuscates the real changes.  Sorry, but I couldn't have found the
bugs without it and it might be useful in the future.

Also, I needed a native method to walk the stack efficiently to find
the caller's class loader.  This will clearly cause problems for
Classpath.  I'm hoping that we'll get a cleaner way to do this.

For Tom Tromey: any method that needs to find its caller needs to be
compiled without sibcalls,so we need some common infrastructure for
this.

Andrew.


        * java/io/ObjectOutputStream.java: Add DEBUG statements
        everywhere.
        (dumpElementln): New method.
        (depth): New field.
        * java/io/ObjectInputStream.java
        (currentClassLoader): Make native.
        (callersClassLoader): New field.
        (depth): New field.
        * java/io/natObjectInputStream.cc (getCallersClassLoader): New
        method.

        (readObject): ENDBLOCKDATA is generated if the class has a write
        method, not if it has a read method.

Index: java/io/ObjectInputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/io/ObjectInputStream.java,v
retrieving revision 1.31
diff -c -2 -p -w -r1.31 ObjectInputStream.java
*** java/io/ObjectInputStream.java      20 Apr 2004 11:37:41 -0000      1.31
--- java/io/ObjectInputStream.java      17 Jun 2004 14:45:57 -0000
*************** exception statement from your version. *
*** 39,51 ****
  package java.io;
  
- import gnu.classpath.Configuration;
- import gnu.java.io.ObjectIdentityWrapper;
- 
  import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.lang.reflect.Proxy;
  import java.util.Arrays;
  import java.util.Hashtable;
--- 39,47 ----
  package java.io;
  
  import java.lang.reflect.Array;
  import java.lang.reflect.Modifier;
  import java.lang.reflect.Proxy;
+ import java.security.PrivilegedAction;
+ import java.security.AccessController;
  import java.util.Arrays;
  import java.util.Hashtable;
*************** import java.util.Vector;
*** 53,56 ****
--- 49,60 ----
  
  
+ import gnu.java.io.ObjectIdentityWrapper;
+ import gnu.java.lang.reflect.TypeSignature;
+ import java.lang.reflect.Field;
+ import java.lang.reflect.Method;
+ import java.lang.reflect.InvocationTargetException;
+ 
+ import gnu.classpath.Configuration;
+ 
  public class ObjectInputStream extends InputStream
    implements ObjectInput, ObjectStreamConstants
*************** public class ObjectInputStream extends I
*** 121,124 ****
--- 125,137 ----
    public final Object readObject() throws ClassNotFoundException, IOException
    {
+       if (callersClassLoader == null)
+       {
+         callersClassLoader = getCallersClassLoader ();
+         if (Configuration.DEBUG && dump)
+           {
+             dumpElementln ("CallersClassLoader = " + callersClassLoader);
+           }
+       }
+ 
      if (this.useSubclassMethod)
        return readObjectOverride();
*************** public class ObjectInputStream extends I
*** 135,138 ****
--- 148,154 ----
  
      byte marker = this.realInputStream.readByte();
+ 
+     depth += 2;
+ 
      if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " ");
  
*************** public class ObjectInputStream extends I
*** 152,158 ****
            {
              if (marker == TC_BLOCKDATALONG)
!               if(dump) dumpElementln("BLOCKDATALONG");
              else
!               if(dump) dumpElementln("BLOCKDATA");
              readNextBlock(marker);
              throw new StreamCorruptedException("Unexpected blockData");
--- 168,174 ----
            {
              if (marker == TC_BLOCKDATALONG)
!               { if(dump) dumpElementln("BLOCKDATALONG"); }
              else
!               { if(dump) dumpElementln("BLOCKDATA"); }
              readNextBlock(marker);
              throw new StreamCorruptedException("Unexpected blockData");
*************** public class ObjectInputStream extends I
*** 320,323 ****
--- 336,342 ----
              
              int handle = assignNewHandle(obj);
+             Object prevObject = this.currentObject;
+             ObjectStreamClass prevObjectStreamClass = 
this.currentObjectStreamClass;
+             
              this.currentObject = obj;
              ObjectStreamClass[] hierarchy =
*************** public class ObjectInputStream extends I
*** 342,350 ****
                      callReadMethod(readObjectMethod, 
this.currentObjectStreamClass.forClass(), obj);
                      setBlockDataMode(oldmode);
                      if(dump) dumpElement("ENDBLOCKDATA? ");
                      try
                        {
!                         // FIXME: XXX: This try block is to catch EOF which is
!                         // thrown for some objects.  That indicates a bug in 
the logic.
                          if (this.realInputStream.readByte() != 
TC_ENDBLOCKDATA)
                            throw new IOException
--- 361,380 ----
                      callReadMethod(readObjectMethod, 
this.currentObjectStreamClass.forClass(), obj);
                      setBlockDataMode(oldmode);
+                   }
+                 else
+                   {
+                     readFields(obj, currentObjectStreamClass);
+                   }
+ 
+                 if (this.currentObjectStreamClass.hasWriteMethod())
+                   {
                      if(dump) dumpElement("ENDBLOCKDATA? ");
                      try
                        {
!                         // FIXME: XXX: This try block is to
!                         // catch EOF which is thrown for some
!                         // objects.  That indicates a bug in
!                         // the logic.
! 
                          if (this.realInputStream.readByte() != 
TC_ENDBLOCKDATA)
                            throw new IOException
*************** public class ObjectInputStream extends I
*** 352,359 ****
                          if(dump) dumpElementln("yes");
                        }
!                     catch (EOFException e)
!                       {
!                         if(dump) dumpElementln("no, got EOFException");
!                       }
                      catch (IOException e)
                        {
--- 382,389 ----
                          if(dump) dumpElementln("yes");
                        }
! //                  catch (EOFException e)
! //                    {
! //                      if(dump) dumpElementln("no, got EOFException");
! //                    }
                      catch (IOException e)
                        {
*************** public class ObjectInputStream extends I
*** 361,373 ****
                        }
                    }
-                 else
-                   {
-                     readFields(obj, currentObjectStreamClass);
-                   }
                }
  
!             this.currentObject = null;
!             this.currentObjectStreamClass = null;
              ret_val = processResolution(osc, obj, handle);
              break;
            }
--- 391,400 ----
                        }
                    }
                }
  
!             this.currentObject = prevObject;
!             this.currentObjectStreamClass = prevObjectStreamClass;
              ret_val = processResolution(osc, obj, handle);
+                 
              break;
            }
*************** public class ObjectInputStream extends I
*** 398,401 ****
--- 425,430 ----
        this.isDeserializing = was_deserializing;
  
+       depth -= 2;
+       
        if (! was_deserializing)
          {
*************** public class ObjectInputStream extends I
*** 711,715 ****
      throws ClassNotFoundException, IOException
    {
!     return Class.forName(osc.getName(), true, currentLoader());
    }
  
--- 740,744 ----
      throws ClassNotFoundException, IOException
    {
!     return Class.forName(osc.getName(), true, callersClassLoader);
    }
  
*************** public class ObjectInputStream extends I
*** 1803,1811 ****
     * @return The current class loader in the calling stack.
     */
!   private static ClassLoader currentClassLoader (SecurityManager sm)
!   {
!     // FIXME: This is too simple.
!     return ClassLoader.getSystemClassLoader ();
!   }
  
    private void callReadMethod (Method readObject, Class klass, Object obj) 
throws IOException
--- 1832,1838 ----
     * @return The current class loader in the calling stack.
     */
!   private static native ClassLoader currentClassLoader (SecurityManager sm);
!   
!   private native ClassLoader getCallersClassLoader();
  
    private void callReadMethod (Method readObject, Class klass, Object obj) 
throws IOException
*************** public class ObjectInputStream extends I
*** 1865,1868 ****
--- 1892,1899 ----
    private static boolean dump = false && Configuration.DEBUG;
  
+   private ClassLoader callersClassLoader;
+ 
+   private int depth = 0;
+ 
    private void dumpElement (String msg)
    {   
*************** public class ObjectInputStream extends I
*** 1873,1876 ****
--- 1904,1910 ----
    {
      System.out.println(msg);
+     for (int i = 0; i < depth; i++)
+       System.out.print (" ");
+     System.out.print (Thread.currentThread() + ": ");
    }
  
Index: java/io/ObjectOutputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/io/ObjectOutputStream.java,v
retrieving revision 1.24
diff -c -2 -p -w -r1.24 ObjectOutputStream.java
*** java/io/ObjectOutputStream.java     31 Dec 2003 11:04:21 -0000      1.24
--- java/io/ObjectOutputStream.java     17 Jun 2004 14:45:57 -0000
*************** public class ObjectOutputStream extends 
*** 145,148 ****
--- 145,155 ----
      useSubclassMethod = false;
      writeStreamHeader();
+ 
+     if (Configuration.DEBUG)
+       {
+       String val = System.getProperty("gcj.dumpobjects");
+       if (val != null && !val.equals(""))
+         dump = true;
+       }
    }
  
*************** public class ObjectOutputStream extends 
*** 173,180 ****
--- 180,195 ----
      if (useSubclassMethod)
        {
+       if (dump)
+         dumpElementln ("WRITE OVERRIDE: " + obj);
+         
        writeObjectOverride(obj);
        return;
        }
  
+     if (dump)
+       dumpElementln ("WRITE: " + obj);
+     
+     depth += 2;    
+ 
      boolean was_serializing = isSerializing;
      boolean old_mode = setBlockDataMode(false);
*************** public class ObjectOutputStream extends 
*** 319,322 ****
--- 334,339 ----
            if (obj instanceof Serializable)
              {
+               Object prevObject = this.currentObject;
+               ObjectStreamClass prevObjectStreamClass = 
this.currentObjectStreamClass;
                currentObject = obj;
                ObjectStreamClass[] hierarchy =
*************** public class ObjectOutputStream extends 
*** 330,344 ****
                    if (currentObjectStreamClass.hasWriteMethod())
                      {
                        setBlockDataMode(true);
                        callWriteMethod(obj, currentObjectStreamClass);
                        setBlockDataMode(false);
                        realOutput.writeByte(TC_ENDBLOCKDATA);
                      }
                    else
                      writeFields(obj, currentObjectStreamClass);
                  }
  
!               currentObject = null;
!               currentObjectStreamClass = null;
                currentPutField = null;
                break;
--- 347,369 ----
                    if (currentObjectStreamClass.hasWriteMethod())
                      {
+                       if (dump)
+                         dumpElementln ("WRITE METHOD CALLED FOR: " + obj);
                        setBlockDataMode(true);
                        callWriteMethod(obj, currentObjectStreamClass);
                        setBlockDataMode(false);
                        realOutput.writeByte(TC_ENDBLOCKDATA);
+                       if (dump)
+                         dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj);
                      }
                    else
+                     {
+                       if (dump)
+                         dumpElementln ("WRITE FIELDS CALLED FOR: " + obj);
                        writeFields(obj, currentObjectStreamClass);
                      }
+                 }
  
!               this.currentObject = prevObject;
!               this.currentObjectStreamClass = prevObjectStreamClass;
                currentPutField = null;
                break;
*************** public class ObjectOutputStream extends 
*** 361,370 ****
        try
          {
            writeObject(e);
          }
        catch (IOException ioe)
          {
!           throw new StreamCorruptedException
!             ("Exception " + ioe + " thrown while exception was being written 
to stream.");
          }
  
--- 386,405 ----
        try
          {
+           if (Configuration.DEBUG)
+             {
+               e.printStackTrace(System.out);
+             }
            writeObject(e);
          }
        catch (IOException ioe)
          {
!           StreamCorruptedException ex = 
!             new StreamCorruptedException
!             (ioe + " thrown while exception was being written to stream.");
!           if (Configuration.DEBUG)
!             {
!               ex.printStackTrace(System.out);
!             }
!           throw ex;
          }
  
*************** public class ObjectOutputStream extends 
*** 376,379 ****
--- 411,418 ----
        isSerializing = was_serializing;
        setBlockDataMode(old_mode);
+       depth -= 2;
+ 
+       if (dump)
+         dumpElementln ("END: " + obj);
        }
    }
*************** public class ObjectOutputStream extends 
*** 1172,1175 ****
--- 1211,1217 ----
        type = fields[i].getType();
  
+       if (dump)
+         dumpElementln ("WRITE FIELD: " + field_name + " type=" + type);
+ 
        if (type == Boolean.TYPE)
          realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), 
field_name));
*************** public class ObjectOutputStream extends 
*** 1513,1516 ****
--- 1555,1566 ----
    }
  
+   private void dumpElementln (String msg)
+   {
+     for (int i = 0; i < depth; i++)
+       System.out.print (" ");
+     System.out.print (Thread.currentThread() + ": ");
+     System.out.println(msg);
+   }
+ 
    // this value comes from 1.2 spec, but is used in 1.1 as well
    private final static int BUFFER_SIZE = 1024;
*************** public class ObjectOutputStream extends 
*** 1535,1538 ****
--- 1585,1592 ----
    private boolean useSubclassMethod;
  
+   private int depth = 0;
+ 
+   private boolean dump = false;
+ 
    static
    {
Index: java/io/natObjectInputStream.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/io/natObjectInputStream.cc,v
retrieving revision 1.7
diff -c -2 -p -w -r1.7 natObjectInputStream.cc
*** java/io/natObjectInputStream.cc     20 Apr 2004 01:38:45 -0000      1.7
--- java/io/natObjectInputStream.cc     17 Jun 2004 14:45:57 -0000
*************** details.  */
*** 20,23 ****
--- 20,25 ----
  #include <java/lang/reflect/Modifier.h>
  #include <java/lang/reflect/Method.h>
+ #include <java/lang/ArrayIndexOutOfBoundsException.h>
+ #include <java/lang/SecurityManager.h>
  
  #ifdef DEBUG
*************** java::io::ObjectInputStream::allocateObj
*** 39,43 ****
        else
        {
!         obj = _Jv_AllocObject (klass);
        }
      }
--- 41,45 ----
        else
        {
!         obj = JvAllocObject (klass);
        }
      }
*************** java::io::ObjectInputStream::callConstru
*** 70,71 ****
--- 72,103 ----
    _Jv_CallAnyMethodA (obj, JvPrimClass (void), meth, false, arg_types, NULL);
  }
+ 
+ java::lang::ClassLoader* 
+ java::io::ObjectInputStream::getCallersClassLoader ()
+ {
+   java::lang::ClassLoader *loader = NULL;
+   gnu::gcj::runtime::StackTrace *t 
+     = new gnu::gcj::runtime::StackTrace(4);
+   java::lang::Class *klass = NULL;
+   try
+     {
+       for (int i = 2; !klass; i++)
+       {
+         klass = t->classAt (i);
+       }
+       loader = klass->getClassLoaderInternal();
+     }
+   catch (::java::lang::ArrayIndexOutOfBoundsException *e)
+     {
+       // FIXME: RuntimeError
+     }
+ 
+   return loader;
+ }
+ 
+ java::lang::ClassLoader*
+ java::io::ObjectInputStream::currentClassLoader 
(::java::lang::SecurityManager *sm)
+ {
+   return sm->currentClassLoader ();
+ }
+ 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]