Re-roll r870801.

*** Original Change Description ***

Reduce DevMode memory footprint by using a WeakInterner for potentially duplicated strings

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


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8850 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/build.xml b/dev/build.xml
index 85feef9..74d6514 100755
--- a/dev/build.xml
+++ b/dev/build.xml
@@ -108,6 +108,7 @@
           <include name="w3c/sac/sac-1.3.jar" />
           <!-- htmlunit dependencies not already included: END -->
           <include name="sun/swingworker/swing-worker-1.1.jar" />
+          <include name="guava/guava-r06/guava-r06-rebased.jar" />
         </fileset>
         <fileset file="build.xml"/>
       </sourcefiles>
@@ -170,6 +171,7 @@
           <zipfileset src="${gwt.tools.lib}/w3c/sac/sac-1.3.jar" />
           <!-- htmlunit dependencies not already included: END -->
           <zipfileset src="${gwt.tools.lib}/sun/swingworker/swing-worker-1.1.jar" />
+          <zipfileset src="${gwt.tools.lib}/guava/guava-r06/guava-r06-rebased.jar" />
         </gwt.jar>
       </sequential>
     </outofdate>
@@ -207,6 +209,7 @@
           <pathelement location="${gwt.tools.lib}/eclipse/jdt-3.4.2.jar" />
           <pathelement location="${gwt.tools.lib}/tomcat/commons-collections-3.1.jar" />
           <pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
+          <pathelement location="${gwt.tools.lib}/guava/guava-r06/guava-r06-rebased.jar" />
       </classpath>
     </gwt.javac>
 
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
index 2e144a9..8748e74 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JAbstractMethod.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.Lists;
 
 import java.lang.annotation.Annotation;
@@ -55,7 +56,7 @@
   JAbstractMethod(String name,
       Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
       JTypeParameter[] jtypeParameters) {
-    this.name = name;
+    this.name = StringInterner.get().intern(name);
     annotations = new Annotations(declaredAnnotations);
 
     if (jtypeParameters != null) {
@@ -239,7 +240,13 @@
     for (int i = 0; i < n; ++i) {
       // Identity tests are ok since identity is durable within an oracle.
       if (params.get(i) == parameter) {
-        return realParameterNames == null ? "arg" + i : realParameterNames[i];
+        String realParameterName;
+        if (realParameterNames == null) {
+          realParameterName = StringInterner.get().intern("arg" + i);
+        } else {
+          realParameterName = StringInterner.get().intern(realParameterNames[i]); 
+        }
+        return realParameterName;
       }
     }
     // TODO: report error if we are asked for an unknown JParameter?
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
index 055d936..ed2afaa 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JArrayType.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import com.google.gwt.dev.util.StringInterner;
+
 import java.lang.annotation.Annotation;
 import java.util.Map;
 
@@ -205,7 +207,8 @@
   @Override
   public String getSimpleSourceName() {
     if (lazySimpleName == null) {
-      lazySimpleName = getComponentType().getSimpleSourceName() + "[]";
+      lazySimpleName = StringInterner.get().intern(
+          getComponentType().getSimpleSourceName() + "[]");
     }
     return lazySimpleName;
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java
index ea912fb..8a3f3d6 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JField.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import com.google.gwt.dev.util.StringInterner;
+
 import java.lang.annotation.Annotation;
 import java.util.Map;
 
@@ -50,7 +52,7 @@
       Map<Class<? extends Annotation>, Annotation> declaredAnnotations) {
     assert (enclosingType != null);
     this.enclosingType = enclosingType;
-    this.name = name;
+    this.name = StringInterner.get().intern(name);
     this.enclosingType.addField(this);
     annotations = new Annotations(declaredAnnotations);
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
index f7b0686..3e30132 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JParameter.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import com.google.gwt.dev.util.StringInterner;
+
 import java.lang.annotation.Annotation;
 import java.util.Map;
 
@@ -37,7 +39,7 @@
   JParameter(JAbstractMethod enclosingMethod, JParameter srcParam) {
     this.enclosingMethod = enclosingMethod;
     this.type = srcParam.type;
-    this.name = srcParam.name;
+    this.name = StringInterner.get().intern(srcParam.name);
     this.annotations = new Annotations(srcParam.annotations);
   }
 
@@ -56,7 +58,7 @@
       boolean argNameIsReal) {
     this.enclosingMethod = enclosingMethod;
     this.type = type;
-    this.name = name;
+    this.name = StringInterner.get().intern(name);
     this.argNameIsReal = argNameIsReal;
 
     enclosingMethod.addParameter(this);
@@ -123,7 +125,7 @@
 
   // Only called by JAbstractMethod after real parameter names are fetched.
   void setName(String name) {
-    this.name = name;
+    this.name = StringInterner.get().intern(name);
   }
 
   // Called when parameter types are found to be parameterized
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
index 1d64d80..b40ab8d 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.IdentitySets;
 import com.google.gwt.dev.util.collect.Lists;
 
@@ -75,7 +76,7 @@
       String enclosingTypeName, String name, boolean isInterface) {
     this.oracle = oracle;
     this.declaringPackage = declaringPackage;
-    this.name = name;
+    this.name = StringInterner.get().intern(name);
     this.isInterface = isInterface;
     if (enclosingTypeName == null) {
       // Add myself to my package.
@@ -244,6 +245,7 @@
       } else {
         lazyQualifiedName = nestedName;
       }
+      lazyQualifiedName = StringInterner.get().intern(lazyQualifiedName);
     }
     return lazyQualifiedName;
   }
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
index 74de83a..1084c4f 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JTypeParameter.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.core.ext.typeinfo;
 
+import com.google.gwt.dev.util.StringInterner;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -28,7 +30,7 @@
   private final String typeName;
 
   public JTypeParameter(String typeName, int ordinal) {
-    this.typeName = typeName;
+    this.typeName = StringInterner.get().intern(typeName);
     this.ordinal = ordinal;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
index 2be3f89..09dfd61 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
@@ -22,6 +22,7 @@
 import com.google.gwt.dev.javac.JdtCompiler.UnitProcessor;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.log.speedtracer.DevModeEventType;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
@@ -81,21 +82,25 @@
 
         MethodArgNamesLookup methodArgs = MethodParamCollector.collect(cud);
 
-        String packageName = Shared.getPackageName(builder.getTypeName());
+        StringInterner interner = StringInterner.get();
+        String packageName = interner.intern(
+            Shared.getPackageName(builder.getTypeName()));
         List<String> unresolvedQualified = new ArrayList<String>();
         List<String> unresolvedSimple = new ArrayList<String>();
         for (char[] simpleRef : cud.compilationResult().simpleNameReferences) {
-          unresolvedSimple.add(canonical(String.valueOf(simpleRef)));
+          unresolvedSimple.add(interner.intern(String.valueOf(simpleRef)));
         }
-        for (char[][] qualifiedRef : cud.compilationResult().qualifiedReferences) {
-          unresolvedQualified.add(canonical(CharOperation.toString(qualifiedRef)));
+        for (char[][] qualifiedRef :
+            cud.compilationResult().qualifiedReferences) {
+          unresolvedQualified.add(
+              interner.intern(CharOperation.toString(qualifiedRef)));
         }
         for (String jsniDep : jsniDeps) {
-          unresolvedQualified.add(canonical(jsniDep));
+          unresolvedQualified.add(interner.intern(jsniDep));
         }
         ArrayList<String> apiRefs = compiler.collectApiRefs(cud);
         for (int i = 0; i < apiRefs.size(); ++i) {
-          apiRefs.set(i, canonical(apiRefs.get(i)));
+          apiRefs.set(i, interner.intern(apiRefs.get(i)));
         }
         Dependencies dependencies = new Dependencies(packageName,
             unresolvedQualified, unresolvedSimple, apiRefs);
@@ -118,15 +123,6 @@
         }
         newlyBuiltUnits.add(unit);
       }
-
-      private String canonical(String str) {
-        String result = internedTypeNames.get(str);
-        if (result != null) {
-          return result;
-        }
-        internedTypeNames.put(str, str);
-        return str;
-      }
     }
 
     /**
@@ -143,14 +139,6 @@
         new UnitProcessorImpl());
 
     /**
-     * Memory efficiency only. Stores canonical versions of dependency type
-     * names so that String instances can be shared among many units. Otherwise,
-     * we'd get many duplicate String objects since we have to build them from
-     * JDT's char arrays.
-     */
-    private final Map<String, String> internedTypeNames = new HashMap<String, String>();
-
-    /**
      * Continuation state for JSNI checking.
      */
     private final JSORestrictionsChecker.CheckerState jsoState = new JSORestrictionsChecker.CheckerState();
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 89d4ec1..a0e0585 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompiledClass.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.util.DiskCache;
 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;
@@ -64,7 +65,8 @@
   CompiledClass(ClassFile classFile, CompiledClass enclosingClass) {
     this.enclosingClass = enclosingClass;
     SourceTypeBinding binding = classFile.referenceBinding;
-    this.internalName = CharOperation.charToString(binding.constantPoolName());
+    this.internalName = StringInterner.get().intern(
+        CharOperation.charToString(binding.constantPoolName()));
     byte[] bytes = classFile.getBytes();
     this.cacheToken = diskCache.writeByteArray(bytes);
     this.isLocal = isLocalType(binding);
@@ -99,7 +101,7 @@
    * Returns the qualified source name, e.g. {@code java.util.Map.Entry}.
    */
   public String getSourceName() {
-    return InternalName.toSourceName(internalName);
+    return StringInterner.get().intern(InternalName.toSourceName(internalName));
   }
 
   public CompilationUnit getUnit() {
diff --git a/dev/core/src/com/google/gwt/dev/javac/Dependencies.java b/dev/core/src/com/google/gwt/dev/javac/Dependencies.java
index 6b7253f..47786a4 100644
--- a/dev/core/src/com/google/gwt/dev/javac/Dependencies.java
+++ b/dev/core/src/com/google/gwt/dev/javac/Dependencies.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.javac;
 
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.HashMap;
 import com.google.gwt.dev.util.collect.Lists;
 
@@ -49,7 +50,8 @@
    */
   Dependencies(String myPackage, List<String> unresolvedQualified,
       List<String> unresolvedSimple, List<String> apiRefs) {
-    this.myPackage = (myPackage.length() == 0) ? "" : (myPackage + '.');
+    this.myPackage = StringInterner.get().intern(
+        (myPackage.length() == 0) ? "" : (myPackage + '.'));
     this.unresolvedQualified = unresolvedQualified;
     this.unresolvedSimple = unresolvedSimple;
     this.apiRefs = apiRefs;
diff --git a/dev/core/src/com/google/gwt/dev/javac/MethodArgNamesLookup.java b/dev/core/src/com/google/gwt/dev/javac/MethodArgNamesLookup.java
index 0b9d667..aba7337 100644
--- a/dev/core/src/com/google/gwt/dev/javac/MethodArgNamesLookup.java
+++ b/dev/core/src/com/google/gwt/dev/javac/MethodArgNamesLookup.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
 import com.google.gwt.dev.javac.asm.CollectMethodData;
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.Maps;
 
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -84,12 +85,13 @@
     int n = method.arguments.length;
     String[] argNames = new String[n];
     for (int i = 0; i < n; ++i) {
-      argNames[i] = String.valueOf(method.arguments[i].name);
+      argNames[i] = StringInterner.get().intern(
+          String.valueOf(method.arguments[i].name));
     }
     StringBuilder buf = new StringBuilder();
     buf.append(enclosingType).append('.').append(method.selector);
     buf.append(method.binding.signature());
-    String key = buf.toString();
+    String key = StringInterner.get().intern(buf.toString());
     methodArgs.put(key, argNames);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java
index e4f92b5..bf4c4af 100644
--- a/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectAnnotationData.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.asm.AnnotationVisitor;
 import com.google.gwt.dev.javac.asm.CollectClassData.AnnotationEnum;
+import com.google.gwt.dev.util.StringInterner;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -37,12 +38,12 @@
     private final boolean visible;
 
     protected AnnotationData(String desc, boolean visible) {
-      this.desc = desc;
+      this.desc = StringInterner.get().intern(desc);
       this.visible = visible;
     }
 
     public void addValue(String name, Object value) {
-      values.put(name, value);
+      values.put(StringInterner.get().intern(name), value);
     }
 
     /**
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java
index 602d020..733edb7 100644
--- a/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectClassData.java
@@ -21,6 +21,7 @@
 import com.google.gwt.dev.asm.Opcodes;
 import com.google.gwt.dev.asm.commons.EmptyVisitor;
 import com.google.gwt.dev.util.Name;
+import com.google.gwt.dev.util.StringInterner;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -114,8 +115,8 @@
      * @param value actual value in this enum
      */
     public AnnotationEnum(String desc, String value) {
-      this.desc = desc;
-      this.value = value;
+      this.desc = StringInterner.get().intern(desc);
+      this.value = StringInterner.get().intern(value);
     }
 
     /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 7e1660e..9d598b1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.Lists;
 
 import java.io.IOException;
@@ -78,7 +79,7 @@
       JType returnType, boolean isAbstract, boolean isStatic, boolean isFinal,
       boolean isPrivate) {
     super(info);
-    this.name = name;
+    this.name = StringInterner.get().intern(name);
     this.enclosingType = enclosingType;
     this.returnType = returnType;
     this.isAbstract = isAbstract;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
index 2faba7d..02cb31e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JParameter.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.jjs.ast;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.util.StringInterner;
 
 /**
  * Java method parameter definition.
@@ -28,8 +29,8 @@
     assert (type != null);
     assert (enclosingMethod != null);
 
-    JParameter x = new JParameter(info, name, type, isFinal, isThis,
-        enclosingMethod);
+    JParameter x = new JParameter(info, StringInterner.get().intern(name), type,
+        isFinal, isThis, enclosingMethod);
 
     enclosingMethod.addParam(x);
     return x;
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsName.java b/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
index 96d7e50..a087d15 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.util.StringInterner;
 
 import java.io.Serializable;
 
@@ -40,8 +41,8 @@
    */
   JsName(JsScope enclosing, String ident, String shortIdent) {
     this.enclosing = enclosing;
-    this.ident = ident;
-    this.shortIdent = shortIdent;
+    this.ident = StringInterner.get().intern(ident);
+    this.shortIdent = StringInterner.get().intern(shortIdent);
     this.isObfuscatable = true;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
index 03c3433..e967138 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.util.StringInterner;
 
 /**
  * Represents a JavaScript expression that references a name.
@@ -36,7 +37,7 @@
 
   public JsNameRef(SourceInfo sourceInfo, String ident) {
     super(sourceInfo);
-    this.ident = ident;
+    this.ident = StringInterner.get().intern(ident);
   }
 
   public String getIdent() {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
index de9552a..b317f38 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.js.JsKeywords;
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.dev.util.collect.Maps;
 
@@ -59,7 +60,7 @@
     if (JsKeywords.isKeyword(ident)) {
       ident = ident + "_$";
     }
-    return ident;
+    return StringInterner.get().intern(ident);
   }
 
   private List<JsScope> children = Collections.emptyList();
@@ -72,7 +73,7 @@
    */
   public JsScope(JsScope parent, String description) {
     assert (parent != null);
-    this.description = description;
+    this.description = StringInterner.get().intern(description);
     this.parent = parent;
     parent.children = Lists.add(parent.children, this);
   }
@@ -81,7 +82,7 @@
    * Subclasses can be parentless.
    */
   protected JsScope(String description) {
-    this.description = description;
+    this.description = StringInterner.get().intern(description);
     this.parent = null;
   }
 
@@ -200,6 +201,8 @@
    * Creates a new name in this scope.
    */
   protected JsName doCreateName(String ident, String shortIdent) {
+    ident = StringInterner.get().intern(ident);
+    shortIdent = StringInterner.get().intern(shortIdent);
     JsName name = new JsName(this, ident, shortIdent);
     names = Maps.putOrdered(names, ident, name);
     return name;
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java
index 1d2ea51..f6fd8cc 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsStringLiteral.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.util.StringInterner;
 
 /**
  * A JavaScript string literal expression.
@@ -27,7 +28,7 @@
   // These only get created by JsProgram so that they can be interned.
   JsStringLiteral(SourceInfo sourceInfo, String value) {
     super(sourceInfo);
-    this.value = value;
+    this.value = StringInterner.get().intern(value);
   }
 
   public String getValue() {
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java
index 0963fc7..a1270ef 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/PathPrefixSet.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.resource.impl;
 
+import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.collect.Maps;
 
 import java.util.ArrayList;
@@ -42,10 +43,11 @@
     private PathPrefix prefix;
 
     public TrieNode(String part) {
-      this.part = part;
+      this.part = StringInterner.get().intern(part);
     }
 
     public TrieNode addChild(String part) {
+      part = StringInterner.get().intern(part);
       TrieNode newChild = new TrieNode(part);
       assert !children.containsKey(part);
       children = Maps.put(children, part, newChild);
diff --git a/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileResource.java b/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileResource.java
index e715bf6..b746913 100644
--- a/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileResource.java
+++ b/dev/core/src/com/google/gwt/dev/resource/impl/ZipFileResource.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.resource.impl;
 
+import com.google.gwt.dev.util.StringInterner;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.zip.ZipEntry;
@@ -31,7 +33,7 @@
   public ZipFileResource(ZipFileClassPathEntry classPathEntry, String path,
       long lastModified) {
     this.classPathEntry = classPathEntry;
-    this.path = path;
+    this.path = StringInterner.get().intern(path);
     this.lastModified = lastModified;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
index 27819b3..d4dbda9 100644
--- a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
+++ b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.shell;
 
 import com.google.gwt.dev.util.JsniRef;
+import com.google.gwt.dev.util.StringInterner;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
@@ -82,7 +83,8 @@
   private void addMemberIfUnique(String name, List<Member> membersForName) {
     if (membersForName.size() == 1) {
       memberById.add(membersForName.get(0));
-      memberIdByName.put(name, memberById.size() - 1);
+      memberIdByName.put(
+          StringInterner.get().intern(name), memberById.size() - 1);
     }
   }
 
@@ -182,7 +184,8 @@
     }
     sb.append(")");
 
-    String mangledName = sb.toString();
+    String mangledName = StringInterner.get().intern(sb.toString());
+    
     return mangledName;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/util/StringInterner.java b/dev/core/src/com/google/gwt/dev/util/StringInterner.java
new file mode 100644
index 0000000..e193293
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/StringInterner.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 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.util;
+
+import com.google.gwt.thirdparty.guava.common.collect.Interner;
+import com.google.gwt.thirdparty.guava.common.collect.Interners;
+
+/**
+ * A utility class for reducing String memory waste. Note that this does not use
+ * the String.intern() method which would prevent GC and fill the PermGen space.
+ * Instead, we use a Google Collections WeakInterner.
+ */
+public class StringInterner {
+  private static final StringInterner instance = new StringInterner();
+
+  public static StringInterner get() {
+    return instance;
+  }
+
+  private final Interner<String> stringPool = Interners.newWeakInterner();
+
+  protected StringInterner() {
+  }
+
+  public String intern(String s) {
+    return stringPool.intern(s);
+  }
+
+}
diff --git a/eclipse/dev/.classpath b/eclipse/dev/.classpath
index 4502d00..022782a 100644
--- a/eclipse/dev/.classpath
+++ b/eclipse/dev/.classpath
@@ -40,5 +40,6 @@
 	<classpathentry kind="var" path="GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-core-js-r5940.jar" sourcepath="/GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-core-js-r5940-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-r5940.jar" sourcepath="/GWT_TOOLS/lib/htmlunit/htmlunit-r5940/htmlunit-r5940-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/protobuf/protobuf-2.2.0/protobuf-java-rebased-2.2.0.jar"/>
+        <classpathentry kind="var" path="GWT_TOOLS/lib/guava/guava-r06/guava-r06-rebased.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>