Some small optimizations. Getting sources still takes the most time of all (~40%), followed by reflective access on the methods and fields of classes. There's not much we can do here, aside from the often-suggested compile-time code-gen DI.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1050 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/Key.java b/src/com/google/inject/Key.java
index 9cfdb77..2422945 100644
--- a/src/com/google/inject/Key.java
+++ b/src/com/google/inject/Key.java
@@ -20,7 +20,6 @@
 import com.google.inject.internal.MoreTypes;
 import static com.google.inject.internal.Preconditions.checkArgument;
 import static com.google.inject.internal.Preconditions.checkNotNull;
-import com.google.inject.internal.ToStringBuilder;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 
@@ -195,10 +194,7 @@
   }
 
   @Override public final String toString() {
-    return new ToStringBuilder(Key.class)
-        .add("type", typeLiteral)
-        .add("annotation", annotationStrategy)
-        .toString();
+    return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
   }
 
   /**
@@ -296,7 +292,7 @@
    * Returns a new key of the specified type with the same annotation as this
    * key.
    */
-  <T> Key<T> ofType(TypeLiteral<T> type) {
+  public <T> Key<T> ofType(TypeLiteral<T> type) {
     return new Key<T>(type, annotationStrategy);
   }
 
diff --git a/src/com/google/inject/TypeLiteral.java b/src/com/google/inject/TypeLiteral.java
index 8036113..27dc48b 100644
--- a/src/com/google/inject/TypeLiteral.java
+++ b/src/com/google/inject/TypeLiteral.java
@@ -82,7 +82,7 @@
   protected TypeLiteral() {
     this.type = getSuperclassTypeParameter(getClass());
     this.rawType = (Class<? super T>) MoreTypes.getRawType(type);
-    this.hashCode = MoreTypes.hashCode(type);
+    this.hashCode = type.hashCode();
   }
 
   /**
@@ -92,7 +92,7 @@
   TypeLiteral(Type type) {
     this.type = canonicalize(checkNotNull(type, "type"));
     this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type);
-    this.hashCode = MoreTypes.hashCode(this.type);
+    this.hashCode = this.type.hashCode();
   }
 
   /**
@@ -151,7 +151,7 @@
   }
 
   @Override public final String toString() {
-    return MoreTypes.toString(type);
+    return MoreTypes.typeToString(type);
   }
 
   /**
diff --git a/src/com/google/inject/internal/InjectorImpl.java b/src/com/google/inject/internal/InjectorImpl.java
index 7938bab..98f3808 100644
--- a/src/com/google/inject/internal/InjectorImpl.java
+++ b/src/com/google/inject/internal/InjectorImpl.java
@@ -54,6 +54,8 @@
  * @see InjectorBuilder
  */
 final class InjectorImpl implements Injector, Lookups {
+  public static final TypeLiteral<String> STRING_TYPE = TypeLiteral.get(String.class);
+
   final State state;
   final InjectorImpl parent;
   final BindingsMultimap bindingsMultimap = new BindingsMultimap();
@@ -271,7 +273,7 @@
   private <T> BindingImpl<T> convertConstantStringBinding(Key<T> key, Errors errors)
       throws ErrorsException {
     // Find a constant string binding.
-    Key<String> stringKey = key.ofType(String.class);
+    Key<String> stringKey = key.ofType(STRING_TYPE);
     BindingImpl<String> stringBinding = state.getExplicitBinding(stringKey);
     if (stringBinding == null || !stringBinding.isConstant()) {
       return null;
diff --git a/src/com/google/inject/internal/MoreTypes.java b/src/com/google/inject/internal/MoreTypes.java
index 3f4b771..78f60ef 100644
--- a/src/com/google/inject/internal/MoreTypes.java
+++ b/src/com/google/inject/internal/MoreTypes.java
@@ -105,9 +105,11 @@
    * type is {@link Serializable}.
    */
   public static Type canonicalize(Type type) {
-    if (type instanceof ParameterizedTypeImpl
-        || type instanceof GenericArrayTypeImpl
-        || type instanceof WildcardTypeImpl) {
+    if (type instanceof Class) {
+      Class<?> c = (Class<?>) type;
+      return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
+
+    } else if (type instanceof CompositeType) {
       return type;
 
     } else if (type instanceof ParameterizedType) {
@@ -119,10 +121,6 @@
       GenericArrayType g = (GenericArrayType) type;
       return new GenericArrayTypeImpl(g.getGenericComponentType());
 
-    } else if (type instanceof Class && ((Class<?>) type).isArray()) {
-      Class<?> c = (Class<?>) type;
-      return new GenericArrayTypeImpl(c.getComponentType());
-
     } else if (type instanceof WildcardType) {
       WildcardType w = (WildcardType) type;
       return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
@@ -222,86 +220,12 @@
     }
   }
 
-  /**
-   * Returns the hashCode of {@code type}.
-   */
-  public static int hashCode(Type type) {
-    if (type instanceof Class) {
-      // Class specifies hashCode().
-      return type.hashCode();
-
-    } else if (type instanceof ParameterizedType) {
-      ParameterizedType p = (ParameterizedType) type;
-      return Arrays.hashCode(p.getActualTypeArguments())
-          ^ p.getRawType().hashCode()
-          ^ hashCodeOrZero(p.getOwnerType());
-
-    } else if (type instanceof GenericArrayType) {
-      return hashCode(((GenericArrayType) type).getGenericComponentType());
-
-    } else if (type instanceof WildcardType) {
-      WildcardType w = (WildcardType) type;
-      return Arrays.hashCode(w.getLowerBounds()) ^ Arrays.hashCode(w.getUpperBounds());
-
-    } else {
-      // This isn't a type we support. Probably a type variable
-      return hashCodeOrZero(type);
-    }
-  }
-
   private static int hashCodeOrZero(Object o) {
     return o != null ? o.hashCode() : 0;
   }
 
-  public static String toString(Type type) {
-    if (type instanceof Class<?>) {
-      return ((Class) type).getName();
-
-    } else if (type instanceof ParameterizedType) {
-      ParameterizedType parameterizedType = (ParameterizedType) type;
-      Type[] arguments = parameterizedType.getActualTypeArguments();
-      Type ownerType = parameterizedType.getOwnerType();
-      StringBuilder stringBuilder = new StringBuilder();
-      if (ownerType != null) {
-        stringBuilder.append(toString(ownerType)).append(".");
-      }
-      stringBuilder.append(toString(parameterizedType.getRawType()));
-      if (arguments.length > 0) {
-        stringBuilder
-            .append("<")
-            .append(toString(arguments[0]));
-        for (int i = 1; i < arguments.length; i++) {
-          stringBuilder.append(", ").append(toString(arguments[i]));
-        }
-      }
-      return stringBuilder.append(">").toString();
-
-    } else if (type instanceof GenericArrayType) {
-      return toString(((GenericArrayType) type).getGenericComponentType()) + "[]";
-
-    } else if (type instanceof WildcardType) {
-      WildcardType wildcardType = (WildcardType) type;
-      Type[] lowerBounds = wildcardType.getLowerBounds();
-      Type[] upperBounds = wildcardType.getUpperBounds();
-
-      if (upperBounds.length != 1 || lowerBounds.length > 1) {
-        throw new UnsupportedOperationException("Unsupported wildcard type " + type);
-      }
-
-      if (lowerBounds.length == 1) {
-        if (upperBounds[0] != Object.class) {
-          throw new UnsupportedOperationException("Unsupported wildcard type " + type);
-        }
-        return "? super " + toString(lowerBounds[0]);
-      } else if (upperBounds[0] == Object.class) {
-        return "?";
-      } else {
-        return "? extends " + toString(upperBounds[0]);
-      }
-
-    } else {
-      return type.toString();
-    }
+  public static String typeToString(Type type) {
+    return type instanceof Class ? ((Class) type).getName() : type.toString();
   }
 
   /**
@@ -508,11 +432,26 @@
     }
 
     @Override public int hashCode() {
-      return MoreTypes.hashCode(this);
+      return Arrays.hashCode(typeArguments)
+          ^ rawType.hashCode()
+          ^ hashCodeOrZero(ownerType);
     }
 
     @Override public String toString() {
-      return MoreTypes.toString(this);
+      StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
+      if (ownerType != null) {
+        stringBuilder.append(typeToString(ownerType)).append(".");
+      }
+      stringBuilder.append(typeToString(rawType));
+      if (typeArguments.length > 0) {
+        stringBuilder
+            .append("<")
+            .append(typeToString(typeArguments[0]));
+        for (int i = 1; i < typeArguments.length; i++) {
+          stringBuilder.append(", ").append(typeToString(typeArguments[i]));
+        }
+      }
+      return stringBuilder.append(">").toString();
     }
 
     private static final long serialVersionUID = 0;
@@ -540,11 +479,11 @@
     }
 
     @Override public int hashCode() {
-      return MoreTypes.hashCode(this);
+      return componentType.hashCode();
     }
 
     @Override public String toString() {
-      return MoreTypes.toString(this);
+      return typeToString(componentType) + "[]";
     }
 
     private static final long serialVersionUID = 0;
@@ -597,11 +536,19 @@
     }
 
     @Override public int hashCode() {
-      return MoreTypes.hashCode(this);
+      // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());  
+      return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
+          ^ (31 + upperBound.hashCode());
     }
 
     @Override public String toString() {
-      return MoreTypes.toString(this);
+      if (lowerBound != null) {
+        return "? super " + typeToString(lowerBound);
+      } else if (upperBound == Object.class) {
+        return "?";
+      } else {
+        return "? extends " + typeToString(upperBound);
+      }
     }
 
     private static final long serialVersionUID = 0;
diff --git a/src/com/google/inject/internal/WeakKeySet.java b/src/com/google/inject/internal/WeakKeySet.java
index 32e0e93..58a9c2c 100644
--- a/src/com/google/inject/internal/WeakKeySet.java
+++ b/src/com/google/inject/internal/WeakKeySet.java
@@ -33,13 +33,18 @@
    * keys whose class names are equal but class loaders are different. This shouldn't be an issue
    * in practice.
    */
-  private Set<String> backingSet = Sets.newHashSet();
+  private Set<String> backingSet;
 
   public boolean add(Key<?> key) {
+    if (backingSet == null) {
+      backingSet = Sets.newHashSet();
+    }
     return backingSet.add(key.toString());
   }
 
   public boolean contains(Object o) {
-    return o instanceof Key && backingSet.contains(o.toString());
+    // avoid calling key.toString() if the backing set is empty. toString is expensive in aggregate,
+    // and most WeakKeySets are empty in practice (because they're used by top-level injectors)
+    return backingSet != null && o instanceof Key && backingSet.contains(o.toString());
   }
 }
diff --git a/test/com/google/inject/util/TypesTest.java b/test/com/google/inject/util/TypesTest.java
index 9168021..546f061 100644
--- a/test/com/google/inject/util/TypesTest.java
+++ b/test/com/google/inject/util/TypesTest.java
@@ -153,14 +153,14 @@
   }
 
   public void testToString() {
-    Assert.assertEquals("java.lang.String", MoreTypes.toString(String.class));
-    assertEquals("java.lang.String[][]", MoreTypes.toString(stringArray));
+    Assert.assertEquals("java.lang.String", MoreTypes.typeToString(String.class));
+    assertEquals("java.lang.String[][]", MoreTypes.typeToString(stringArray));
     assertEquals("java.util.Map<java.lang.String, java.lang.Integer>",
-        MoreTypes.toString(mapStringInteger));
+        MoreTypes.typeToString(mapStringInteger));
     assertEquals("java.util.List<java.lang.String[][]>",
-        MoreTypes.toString(listStringArray));
+        MoreTypes.typeToString(listStringArray));
     assertEquals(innerFloatDouble.toString(),
-        MoreTypes.toString(innerFloatDouble));
+        MoreTypes.typeToString(innerFloatDouble));
   }
 
   static class Owning<A> {}