Modifies CompiledClass to be serializable

Review at http://gwt-code-reviews.appspot.com/1329802


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9681 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
index a0e0585..6779fed 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -19,57 +19,51 @@
 import com.google.gwt.dev.util.Name.InternalName;
 import com.google.gwt.dev.util.StringInterner;
 
-import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.internal.compiler.ClassFile;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
-import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
 
 /**
  * Encapsulates the state of a single compiled class file.
  */
-public final class CompiledClass {
+public final class CompiledClass implements Serializable {
 
   private static final DiskCache diskCache = new DiskCache();
 
+  private final CompiledClass enclosingClass;
+  private final String internalName;
+  private final boolean isLocal;
+  private CompilationUnit unit;
+  
   /**
-   * Returns <code>true</code> if this is a local type, or if this type is
-   * nested inside of any local type.
+   * A token to retrieve this object's bytes from the disk cache. byte code is
+   * placed in the cache when the object is deserialized.
    */
-  private static boolean isLocalType(SourceTypeBinding binding) {
-    SourceTypeBinding b = binding;
-    while (!b.isStatic()) {
-      if (b instanceof LocalTypeBinding) {
-        return true;
-      }
-      b = ((NestedTypeBinding) b).enclosingType;
-    }
-    return false;
-  }
-
-  protected final CompiledClass enclosingClass;
-  protected final String internalName;
-  protected final boolean isLocal;
-  protected CompilationUnit unit;
-
-  /**
-   * A token to retrieve this object's bytes from the disk cache.
-   */
-  private final long cacheToken;
-
+  private transient long cacheToken;
   private transient NameEnvironmentAnswer nameEnvironmentAnswer;
 
-  CompiledClass(ClassFile classFile, CompiledClass enclosingClass) {
+  /**
+   * Create a compiled class from raw class bytes.
+   * 
+   * @param classBytes - byte code for this class
+   * @param enclosingClass - outer class
+   * @param isLocal Is this class a local class? (See the JLS rev 2 section
+   *          14.3)
+   * @param internalName the internal binary name for this class. e.g. {@code
+   *          java/util/Map$Entry}. See
+   *          {@link "http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#14757"}
+   */
+  CompiledClass(byte[] classBytes, CompiledClass enclosingClass,
+      boolean isLocal, String internalName) {
     this.enclosingClass = enclosingClass;
-    SourceTypeBinding binding = classFile.referenceBinding;
-    this.internalName = StringInterner.get().intern(
-        CharOperation.charToString(binding.constantPoolName()));
-    byte[] bytes = classFile.getBytes();
-    this.cacheToken = diskCache.writeByteArray(bytes);
-    this.isLocal = isLocalType(binding);
+    this.internalName = StringInterner.get().intern(internalName);
+    this.cacheToken = diskCache.writeByteArray(classBytes);
+    this.isLocal = isLocal;
   }
 
   /**
@@ -84,7 +78,8 @@
   }
 
   /**
-   * Returns the binary class name, e.g. {@code java/util/Map$Entry}.
+   * Returns the class internal binary name for this type, e.g. {@code
+   * java/util/Map$Entry}.
    */
   public String getInternalName() {
     return internalName;
@@ -138,4 +133,16 @@
     assert this.unit == null;
     this.unit = unit;
   }
+
+  private void readObject(ObjectInputStream inputStream)
+      throws ClassNotFoundException, IOException {
+    inputStream.defaultReadObject();
+    this.cacheToken = diskCache.transferFromStream(inputStream);
+  }
+
+  private void writeObject(ObjectOutputStream outputStream) throws IOException {
+    outputStream.defaultWriteObject();
+    byte[] byteCode = diskCache.readByteArray(cacheToken);
+    diskCache.transferToStream(cacheToken, outputStream);
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
index fb752ef..464f8b0 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -52,8 +52,10 @@
 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
 import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
 import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
 import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
 import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.UnresolvedReferenceBinding;
@@ -129,14 +131,6 @@
     }
   }
   /**
-   * Interface for processing units on the fly during compilation.
-   */
-  public interface UnitProcessor {
-    void process(CompilationUnitBuilder builder,
-        CompilationUnitDeclaration cud, List<CompiledClass> compiledClasses);
-  }
-
-  /**
    * Static cache of all the JRE package names.
    */
   public static class JreIndex {
@@ -181,6 +175,14 @@
   }
 
   /**
+   * Interface for processing units on the fly during compilation.
+   */
+  public interface UnitProcessor {
+    void process(CompilationUnitBuilder builder,
+        CompilationUnitDeclaration cud, List<CompiledClass> compiledClasses);
+  }
+
+  /**
    * Adapts a {@link CompilationUnit} for a JDT compile.
    */
   private static class Adapter implements ICompilationUnit {
@@ -261,7 +263,9 @@
         enclosingClass = results.get(enclosingClassFile);
         assert enclosingClass != null;
       }
-      CompiledClass result = new CompiledClass(classFile, enclosingClass);
+      String internalName = CharOperation.charToString(classFile.fileName());
+      CompiledClass result = new CompiledClass(classFile.getBytes(), 
+          enclosingClass, isLocalType(classFile), internalName);
       results.put(classFile, result);
     }
   }
@@ -357,7 +361,7 @@
       }
     }
   }
-
+  
   /**
    * Compiles the given set of units. The units will be internally modified to
    * reflect the results of compilation.
@@ -439,6 +443,21 @@
     return null;
   }
 
+  /**
+   * Returns <code>true</code> if this is a local type, or if this type is
+   * nested inside of any local type.
+   */
+  private static boolean isLocalType(ClassFile classFile) {
+    SourceTypeBinding b = classFile.referenceBinding;
+    while (!b.isStatic()) {
+      if (b instanceof LocalTypeBinding) {
+        return true;
+      }
+      b = ((NestedTypeBinding) b).enclosingType;
+    }
+    return false;
+  }
+
   private AdditionalTypeProviderDelegate additionalTypeProviderDelegate;
 
   /**
diff --git a/dev/core/test/com/google/gwt/dev/javac/CompiledClassTest.java b/dev/core/test/com/google/gwt/dev/javac/CompiledClassTest.java
new file mode 100644
index 0000000..8d78d85
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/javac/CompiledClassTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.javac;
+
+import com.google.gwt.dev.util.Util;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+public class CompiledClassTest extends TestCase {
+  static byte[] dummyByteCode = {
+    (byte) 0xDE, (byte) 0xAD, (byte)0xBE, (byte)0xEF  
+  };
+  static final String DUMMY_NAME = "com.example.DeadBeef";
+  
+  public void testCompiledClassSerialization() throws Exception {
+    CompiledClass writeObject = new CompiledClass(dummyByteCode, null, false, DUMMY_NAME);
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    Util.writeObjectToStream(outputStream, writeObject);
+    ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
+    CompiledClass readObject = Util.readStreamAsObject(inputStream, CompiledClass.class);
+    assertEquals(4, readObject.getBytes().length);
+    byte[] readBytes = readObject.getBytes();
+    for (int i = 0; i < 4 ; ++i) {
+      assertEquals(dummyByteCode[i], readBytes[i]);
+    }
+  }
+}