Change Type, Method and Field to be IDs rather than values.

This makes it possible to declare constants for types like
void and int.

http://code.google.com/p/android/issues/detail?id=6322

Change-Id: Ic0b6e7d9b89aba40457c3015b75ab24c413a6f33
diff --git a/dx/junit-tests/HelloWorldMaker.java b/dx/junit-tests/HelloWorldMaker.java
index 1f0008b..001f31a 100644
--- a/dx/junit-tests/HelloWorldMaker.java
+++ b/dx/junit-tests/HelloWorldMaker.java
@@ -17,14 +17,21 @@
 import com.android.dx.gen.BinaryOp;
 import com.android.dx.gen.Code;
 import com.android.dx.gen.DexGenerator;
-import com.android.dx.gen.Field;
+import com.android.dx.gen.FieldId;
 import com.android.dx.gen.Local;
-import com.android.dx.gen.Method;
+import com.android.dx.gen.MethodId;
 import com.android.dx.gen.Type;
 import com.android.dx.rop.code.AccessFlags;
 import java.io.PrintStream;
 
 public class HelloWorldMaker {
+    private static final Type<PrintStream> PRINT_STREAM = Type.get(PrintStream.class);
+    private static final FieldId<System, PrintStream> SYSTEM_OUT
+            = Type.get(System.class).getField(PRINT_STREAM, "out");
+    private static final MethodId<Integer, String> TO_HEX_STRING
+            = Type.get(Integer.class).getMethod(Type.STRING, "toHexString", Type.INT);
+    private static final MethodId<PrintStream, Void> PRINTLN
+            = PRINT_STREAM.getMethod(Type.VOID, "println", Type.STRING);
 
     public static void main(String[] args) throws Exception {
 
@@ -46,40 +53,30 @@
         DexGenerator generator = new DexGenerator();
 
         // lookup the symbols of interest
-        Type<Object> object = generator.getType(Object.class);
-        Type<Integer> integer = generator.getType(Integer.class);
-        Type<Integer> intType = generator.getType(int.class);
-        Type<String> string = generator.getType(String.class);
-        Type<Void> voidType = generator.getType(void.class);
-        Type<System> system = generator.getType(System.class);
-        Type<PrintStream> printStream = generator.getType(PrintStream.class);
-        Type<?> helloWorld = generator.getType("LHelloWorld;");
-        Field<System, PrintStream> systemOutField = system.getField(printStream, "out");
-        Method<Integer, String> toHexString = integer.getMethod(string, "toHexString", intType);
-        Method<PrintStream, Void> println = printStream.getMethod(voidType, "println", string);
-        Method hello = helloWorld.getMethod(voidType, "hello");
+        Type<?> helloWorld = Type.get("LHelloWorld;");
+        MethodId hello = helloWorld.getMethod(Type.VOID, "hello");
 
         // create some registers
         //    (I'd like a better syntax for this)
-        Code code = hello.declare(AccessFlags.ACC_STATIC | AccessFlags.ACC_PUBLIC);
-        Local<Integer> a = code.newLocal(intType);
-        Local<Integer> b = code.newLocal(intType);
-        Local<Integer> c = code.newLocal(intType);
-        Local<String> s = code.newLocal(string);
-        Local<PrintStream> localSystemOut = code.newLocal(printStream);
+        Code code = generator.declare(hello, AccessFlags.ACC_STATIC | AccessFlags.ACC_PUBLIC);
+        Local<Integer> a = code.newLocal(Type.INT);
+        Local<Integer> b = code.newLocal(Type.INT);
+        Local<Integer> c = code.newLocal(Type.INT);
+        Local<String> s = code.newLocal(Type.STRING);
+        Local<PrintStream> localSystemOut = code.newLocal(PRINT_STREAM);
 
         // specify the code instruction-by-instruction (approximately)
         code.loadConstant(a, 0xabcd);
         code.loadConstant(b, 0xaaaa);
         code.op(BinaryOp.SUBTRACT, c, a, b);
-        code.invokeStatic(toHexString, s, c);
-        code.sget(systemOutField, localSystemOut);
-        code.invokeVirtual(println, null, localSystemOut, s);
+        code.invokeStatic(TO_HEX_STRING, s, c);
+        code.sget(SYSTEM_OUT, localSystemOut);
+        code.invokeVirtual(PRINTLN, null, localSystemOut, s);
         code.returnVoid();
 
         // TODO: create the constructor
 
-        helloWorld.declare("Generated.java", AccessFlags.ACC_PUBLIC, object);
+        generator.declare(helloWorld, "Generated.java", AccessFlags.ACC_PUBLIC, Type.OBJECT);
 
         // load the dex
         ClassLoader loader = generator.load(HelloWorldMaker.class.getClassLoader());
diff --git a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java b/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java
index 22fb3da..99eea85 100644
--- a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java
+++ b/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java
@@ -23,7 +23,9 @@
 import static com.android.dx.rop.code.AccessFlags.ACC_PUBLIC;
 import static com.android.dx.rop.code.AccessFlags.ACC_STATIC;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -38,23 +40,15 @@
  */
 public final class DexGeneratorTest extends TestCase {
     private DexGenerator generator;
-    private Type<Void> voidType;
-    private Type<Integer> intType;
-    private Type<Long> longType;
-    private Type<Boolean> booleanType;
-    private Type<Float> floatType;
-    private Type<Double> doubleType;
-    private Type<Object> objectType;
-    private Type<String> stringType;
-    private Type<boolean[]> booleanArrayType;
-    private Type<int[]> intArrayType;
-    private Type<long[]> longArrayType;
-    private Type<long[][]> long2dArrayType;
-    private Type<Object[]> objectArrayType;
-    private Type<DexGeneratorTest> dexGeneratorTestType;
-    private Type<?> generatedType;
-    private Type<Callable> callableType;
-    private Method<Callable, Object> call;
+    private static Type<DexGeneratorTest> TEST_TYPE = Type.get(DexGeneratorTest.class);
+    private static Type<?> INT_ARRAY = Type.get(int[].class);
+    private static Type<boolean[]> BOOLEAN_ARRAY = Type.get(boolean[].class);
+    private static Type<long[]> LONG_ARRAY = Type.get(long[].class);
+    private static Type<Object[]> OBJECT_ARRAY = Type.get(Object[].class);
+    private static Type<long[][]> LONG_2D_ARRAY = Type.get(long[][].class);
+    private static Type<?> GENERATED = Type.get("LGenerated;");
+    private static Type<Callable> CALLABLE = Type.get(Callable.class);
+    private static MethodId<Callable, Object> CALL = CALLABLE.getMethod(Type.OBJECT, "call");
 
     @Override protected void setUp() throws Exception {
         super.setUp();
@@ -67,24 +61,7 @@
      */
     private void reset() {
         generator = new DexGenerator();
-        voidType = generator.getType(void.class);
-        intType = generator.getType(int.class);
-        longType = generator.getType(long.class);
-        booleanType = generator.getType(boolean.class);
-        floatType = generator.getType(float.class);
-        doubleType = generator.getType(double.class);
-        objectType = generator.getType(Object.class);
-        stringType = generator.getType(String.class);
-        booleanArrayType = generator.getType(boolean[].class);
-        intArrayType = generator.getType(int[].class);
-        longArrayType = generator.getType(long[].class);
-        long2dArrayType = generator.getType(long[][].class);
-        objectArrayType = generator.getType(Object[].class);
-        dexGeneratorTestType = generator.getType(DexGeneratorTest.class);
-        generatedType = generator.getType("LGenerated;");
-        callableType = generator.getType(Callable.class);
-        call = callableType.getMethod(objectType, "call");
-        generatedType.declare("Generated.java", ACC_PUBLIC, objectType);
+        generator.declare(GENERATED, "Generated.java", ACC_PUBLIC, Type.OBJECT);
     }
 
     public void testNewInstance() throws Exception {
@@ -94,13 +71,14 @@
          *   return result;
          * }
          */
-        Type<Constructable> constructable = generator.getType(Constructable.class);
-        Code code = generatedType.getMethod(constructable, "call", longType, booleanType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Long> localA = code.getParameter(0, longType);
-        Local<Boolean> localB = code.getParameter(1, booleanType);
-        Method<Constructable, Void> constructor
-                = constructable.getConstructor(longType, booleanType);
+        Type<Constructable> constructable = Type.get(Constructable.class);
+        MethodId<?, Constructable> methodId = GENERATED.getMethod(
+                constructable, "call", Type.LONG, Type.BOOLEAN);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Long> localA = code.getParameter(0, Type.LONG);
+        Local<Boolean> localB = code.getParameter(1, Type.BOOLEAN);
+        MethodId<Constructable, Void> constructor
+                = constructable.getConstructor(Type.LONG, Type.BOOLEAN);
         Local<Constructable> localResult = code.newLocal(constructable);
         code.newInstance(localResult, constructor, localA, localB);
         code.returnValue(localResult);
@@ -126,12 +104,12 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(intType, "call", intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localA = code.getParameter(0, intType);
-        Local<Integer> localResult = code.newLocal(intType);
-        Method<?, Integer> staticMethod
-                = dexGeneratorTestType.getMethod(intType, "staticMethod", intType);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localA = code.getParameter(0, Type.INT);
+        Local<Integer> localResult = code.newLocal(Type.INT);
+        MethodId<?, Integer> staticMethod
+                = TEST_TYPE.getMethod(Type.INT, "staticMethod", Type.INT);
         code.invokeStatic(staticMethod, localResult, localA);
         code.returnValue(localResult);
 
@@ -150,13 +128,13 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(intType, "call", dexGeneratorTestType, intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<DexGeneratorTest> localInstance = code.getParameter(0, dexGeneratorTestType);
-        Local<Integer> localA = code.getParameter(1, intType);
-        Local<Integer> localResult = code.newLocal(intType);
-        Method<DexGeneratorTest, Integer> virtualMethod
-                = dexGeneratorTestType.getMethod(intType, "virtualMethod", intType);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", TEST_TYPE, Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<DexGeneratorTest> localInstance = code.getParameter(0, TEST_TYPE);
+        Local<Integer> localA = code.getParameter(1, Type.INT);
+        Local<Integer> localResult = code.newLocal(Type.INT);
+        MethodId<DexGeneratorTest, Integer> virtualMethod
+                = TEST_TYPE.getMethod(Type.INT, "virtualMethod", Type.INT);
         code.invokeVirtual(virtualMethod, localResult, localInstance, localA);
         code.returnValue(localResult);
 
@@ -180,27 +158,27 @@
          *   return b;
          * }
          */
-        Type<G> generated = generator.getType("LGenerated;");
-        Method<G, Integer> directMethod = generated.getMethod(intType, "directMethod");
-        Code directCode = directMethod.declare(ACC_PRIVATE);
+        Type<G> generated = Type.get("LGenerated;");
+        MethodId<G, Integer> directMethodId = generated.getMethod(Type.INT, "directMethod");
+        Code directCode = generator.declare(directMethodId, ACC_PRIVATE);
         directCode.getThis(generated); // 'this' is unused
-        Local<Integer> localA = directCode.newLocal(intType);
+        Local<Integer> localA = directCode.newLocal(Type.INT);
         directCode.loadConstant(localA, 5);
         directCode.returnValue(localA);
 
-        Method<G, Integer> method = generated.getMethod(intType, "call", generated);
-        Code code = method.declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localB = code.newLocal(intType);
+        MethodId<G, Integer> methodId = generated.getMethod(Type.INT, "call", generated);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localB = code.newLocal(Type.INT);
         Local<G> localG = code.getParameter(0, generated);
-        code.invokeDirect(directMethod, localB, localG);
+        code.invokeDirect(directMethodId, localB, localG);
         code.returnValue(localB);
 
         addDefaultConstructor();
 
         Class<?> generatedClass = loadAndGenerate();
         Object instance = generatedClass.newInstance();
-        java.lang.reflect.Method m = generatedClass.getMethod("call", generatedClass);
-        assertEquals(5, m.invoke(null, instance));
+        Method method = generatedClass.getMethod("call", generatedClass);
+        assertEquals(5, method.invoke(null, instance));
     }
 
     public <G> void testInvokeSuper() throws Exception {
@@ -213,16 +191,18 @@
          *   return 0;
          * }
          */
-        Type<G> generated = generator.getType("LGenerated;");
-        Method<Object, Integer> objectHashCode = objectType.getMethod(intType, "hashCode");
-        Code superHashCode = generated.getMethod(intType, "superHashCode").declare(ACC_PUBLIC);
-        Local<Integer> localResult = superHashCode.newLocal(intType);
+        Type<G> generated = Type.get("LGenerated;");
+        MethodId<Object, Integer> objectHashCode = Type.OBJECT.getMethod(Type.INT, "hashCode");
+        Code superHashCode = generator.declare(
+                GENERATED.getMethod(Type.INT, "superHashCode"), ACC_PUBLIC);
+        Local<Integer> localResult = superHashCode.newLocal(Type.INT);
         Local<G> localThis = superHashCode.getThis(generated);
         superHashCode.invokeSuper(objectHashCode, localResult, localThis);
         superHashCode.returnValue(localResult);
 
-        Code generatedHashCode = generated.getMethod(intType, "hashCode").declare(ACC_PUBLIC);
-        Local<Integer> localZero = generatedHashCode.newLocal(intType);
+        Code generatedHashCode = generator.declare(
+                GENERATED.getMethod(Type.INT, "hashCode"), ACC_PUBLIC);
+        Local<Integer> localZero = generatedHashCode.newLocal(Type.INT);
         generatedHashCode.loadConstant(localZero, 0);
         generatedHashCode.returnValue(localZero);
 
@@ -230,8 +210,8 @@
 
         Class<?> generatedClass = loadAndGenerate();
         Object instance = generatedClass.newInstance();
-        java.lang.reflect.Method m = generatedClass.getMethod("superHashCode");
-        assertEquals(System.identityHashCode(instance), m.invoke(instance));
+        Method method = generatedClass.getMethod("superHashCode");
+        assertEquals(System.identityHashCode(instance), method.invoke(instance));
     }
 
     @SuppressWarnings("unused") // called by generated code
@@ -246,11 +226,11 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(objectType, "call", callableType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Callable> localC = code.getParameter(0, callableType);
-        Local<Object> localResult = code.newLocal(objectType);
-        code.invokeInterface(call, localResult, localC);
+        MethodId<?, Object> methodId = GENERATED.getMethod(Type.OBJECT, "call", CALLABLE);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Callable> localC = code.getParameter(0, CALLABLE);
+        Local<Object> localResult = code.newLocal(Type.OBJECT);
+        code.invokeInterface(CALL, localResult, localC);
         code.returnValue(localResult);
 
         Callable<Object> callable = new Callable<Object>() {
@@ -263,17 +243,17 @@
 
     public void testParameterMismatch() throws Exception {
         Type<?>[] argTypes = {
-                generator.getType(Integer.class), // should fail because the code specifies int
-                objectType,
+                Type.get(Integer.class), // should fail because the code specifies int
+                Type.OBJECT,
         };
-        Method<?, Integer> method = generatedType.getMethod(intType, "call", argTypes);
-        Code code = method.declare(ACC_PUBLIC | ACC_STATIC);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", argTypes);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         try {
-            code.getParameter(0, intType);
+            code.getParameter(0, Type.INT);
         } catch (IllegalArgumentException e) {
         }
         try {
-            code.getParameter(2, intType);
+            code.getParameter(2, Type.INT);
         } catch (IndexOutOfBoundsException e) {
         }
     }
@@ -286,15 +266,15 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(booleanType, "call", dexGeneratorTestType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<DexGeneratorTest> localTest = code.getParameter(0, dexGeneratorTestType);
-        Type<CharSequence> charSequenceType = generator.getType(CharSequence.class);
-        Method<Object, String> objectToString = objectType.getMethod(stringType, "toString");
-        Method<Object, Boolean> objectEquals
-                = objectType.getMethod(booleanType, "equals", objectType);
+        MethodId<?, Boolean> methodId = GENERATED.getMethod(Type.BOOLEAN, "call", TEST_TYPE);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<DexGeneratorTest> localTest = code.getParameter(0, TEST_TYPE);
+        Type<CharSequence> charSequenceType = Type.get(CharSequence.class);
+        MethodId<Object, String> objectToString = Type.OBJECT.getMethod(Type.STRING, "toString");
+        MethodId<Object, Boolean> objectEquals
+                = Type.OBJECT.getMethod(Type.BOOLEAN, "equals", Type.OBJECT);
         Local<CharSequence> localCs = code.newLocal(charSequenceType);
-        Local<Boolean> localResult = code.newLocal(booleanType);
+        Local<Boolean> localResult = code.newLocal(Type.BOOLEAN);
         code.invokeVirtual(objectToString, localCs, localTest);
         code.invokeVirtual(objectEquals, localResult, localCs, localTest);
         code.returnValue(localResult);
@@ -303,10 +283,10 @@
     }
 
     public void testReturnTypeMismatch() {
-        Code code = generatedType.getMethod(stringType, "call")
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        MethodId<?, String> methodId = GENERATED.getMethod(Type.STRING, "call");
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         try {
-            code.returnValue(code.newLocal(booleanType));
+            code.returnValue(code.newLocal(Type.BOOLEAN));
             fail();
         } catch (IllegalArgumentException expected) {
         }
@@ -324,15 +304,15 @@
          *   protected static Object b;
          * }
          */
-        generatedType.getField(intType, "a").declare(ACC_PUBLIC | ACC_STATIC, 3);
-        generatedType.getField(objectType, "b").declare(ACC_PROTECTED | ACC_STATIC, null);
+        generator.declare(GENERATED.getField(Type.INT, "a"), ACC_PUBLIC | ACC_STATIC, 3);
+        generator.declare(GENERATED.getField(Type.OBJECT, "b"), ACC_PROTECTED | ACC_STATIC, null);
         Class<?> generatedClass = loadAndGenerate();
 
-        java.lang.reflect.Field a = generatedClass.getField("a");
+        Field a = generatedClass.getField("a");
         assertEquals(int.class, a.getType());
         assertEquals(3, a.get(null));
 
-        java.lang.reflect.Field b = generatedClass.getDeclaredField("b");
+        Field b = generatedClass.getDeclaredField("b");
         assertEquals(Object.class, b.getType());
         b.setAccessible(true);
         assertEquals(null, b.get(null));
@@ -345,19 +325,19 @@
          *   protected Object b;
          * }
          */
-        generatedType.getField(intType, "a").declare(ACC_PUBLIC, null);
-        generatedType.getField(objectType, "b").declare(ACC_PROTECTED, null);
+        generator.declare(GENERATED.getField(Type.INT, "a"), ACC_PUBLIC, null);
+        generator.declare(GENERATED.getField(Type.OBJECT, "b"), ACC_PROTECTED, null);
 
         addDefaultConstructor();
 
         Class<?> generatedClass = loadAndGenerate();
         Object instance = generatedClass.newInstance();
 
-        java.lang.reflect.Field a = generatedClass.getField("a");
+        Field a = generatedClass.getField("a");
         assertEquals(int.class, a.getType());
         assertEquals(0, a.get(instance));
 
-        java.lang.reflect.Field b = generatedClass.getDeclaredField("b");
+        Field b = generatedClass.getDeclaredField("b");
         assertEquals(Object.class, b.getType());
         b.setAccessible(true);
         assertEquals(null, b.get(instance));
@@ -376,18 +356,19 @@
          *   }
          * }
          */
-        Type<G> generated = generator.getType("LGenerated;");
-        Field<G, Integer> field = generated.getField(intType, "a");
-        field.declare(ACC_PUBLIC | ACC_FINAL, null);
-        Code code = generatedType.getConstructor(intType).declare(ACC_PUBLIC | ACC_CONSTRUCTOR);
+        Type<G> generated = Type.get("LGenerated;");
+        FieldId<G, Integer> fieldId = generated.getField(Type.INT, "a");
+        generator.declare(fieldId, ACC_PUBLIC | ACC_FINAL, null);
+        MethodId<?, Void> constructor = GENERATED.getConstructor(Type.INT);
+        Code code = generator.declare(constructor, ACC_PUBLIC | ACC_CONSTRUCTOR);
         Local<G> thisRef = code.getThis(generated);
-        Local<Integer> parameter = code.getParameter(0, intType);
-        code.invokeDirect(objectType.getConstructor(), null, thisRef);
-        code.iput(field, thisRef, parameter);
+        Local<Integer> parameter = code.getParameter(0, Type.INT);
+        code.invokeDirect(Type.OBJECT.getConstructor(), null, thisRef);
+        code.iput(fieldId, thisRef, parameter);
         code.returnVoid();
 
         Class<?> generatedClass = loadAndGenerate();
-        java.lang.reflect.Field a = generatedClass.getField("a");
+        Field a = generatedClass.getField("a");
         Object instance = generatedClass.getConstructor(int.class).newInstance(0xabcd);
         assertEquals(0xabcd, a.get(instance));
     }
@@ -414,9 +395,9 @@
          * }
          */
         reset();
-        Type<T> returnType = generator.getType(javaType);
-        Code code = generatedType.getMethod(returnType, "call")
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        Type<T> returnType = Type.get(javaType);
+        Code code = generator.declare(GENERATED.getMethod(returnType, "call"),
+                ACC_PUBLIC | ACC_STATIC);
         if (value != null) {
             Local<T> i = code.newLocal(returnType);
             code.loadConstant(i, value);
@@ -426,44 +407,44 @@
         }
 
         Class<?> generatedClass = loadAndGenerate();
-        java.lang.reflect.Method method = generatedClass.getMethod("call");
+        Method method = generatedClass.getMethod("call");
         assertEquals(javaType, method.getReturnType());
         assertEquals(value, method.invoke(null));
     }
 
     public void testBranching() throws Exception {
-        java.lang.reflect.Method lt = branchingMethod(Comparison.LT);
+        Method lt = branchingMethod(Comparison.LT);
         assertEquals(Boolean.TRUE, lt.invoke(null, 1, 2));
         assertEquals(Boolean.FALSE, lt.invoke(null, 1, 1));
         assertEquals(Boolean.FALSE, lt.invoke(null, 2, 1));
 
-        java.lang.reflect.Method le = branchingMethod(Comparison.LE);
+        Method le = branchingMethod(Comparison.LE);
         assertEquals(Boolean.TRUE, le.invoke(null, 1, 2));
         assertEquals(Boolean.TRUE, le.invoke(null, 1, 1));
         assertEquals(Boolean.FALSE, le.invoke(null, 2, 1));
 
-        java.lang.reflect.Method eq = branchingMethod(Comparison.EQ);
+        Method eq = branchingMethod(Comparison.EQ);
         assertEquals(Boolean.FALSE, eq.invoke(null, 1, 2));
         assertEquals(Boolean.TRUE, eq.invoke(null, 1, 1));
         assertEquals(Boolean.FALSE, eq.invoke(null, 2, 1));
 
-        java.lang.reflect.Method ge = branchingMethod(Comparison.GE);
+        Method ge = branchingMethod(Comparison.GE);
         assertEquals(Boolean.FALSE, ge.invoke(null, 1, 2));
         assertEquals(Boolean.TRUE, ge.invoke(null, 1, 1));
         assertEquals(Boolean.TRUE, ge.invoke(null, 2, 1));
 
-        java.lang.reflect.Method gt = branchingMethod(Comparison.GT);
+        Method gt = branchingMethod(Comparison.GT);
         assertEquals(Boolean.FALSE, gt.invoke(null, 1, 2));
         assertEquals(Boolean.FALSE, gt.invoke(null, 1, 1));
         assertEquals(Boolean.TRUE, gt.invoke(null, 2, 1));
 
-        java.lang.reflect.Method ne = branchingMethod(Comparison.NE);
+        Method ne = branchingMethod(Comparison.NE);
         assertEquals(Boolean.TRUE, ne.invoke(null, 1, 2));
         assertEquals(Boolean.FALSE, ne.invoke(null, 1, 1));
         assertEquals(Boolean.TRUE, ne.invoke(null, 2, 1));
     }
 
-    private java.lang.reflect.Method branchingMethod(Comparison comparison) throws Exception {
+    private Method branchingMethod(Comparison comparison) throws Exception {
         /*
          * public static boolean call(int localA, int localB) {
          *   if (a comparison b) {
@@ -473,11 +454,12 @@
          * }
          */
         reset();
-        Code code = generatedType.getMethod(booleanType, "call", intType, intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localA = code.getParameter(0, intType);
-        Local<Integer> localB = code.getParameter(1, intType);
-        Local<Boolean> result = code.newLocal(generator.getType(boolean.class));
+        MethodId<?, Boolean> methodId = GENERATED.getMethod(
+                Type.BOOLEAN, "call", Type.INT, Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localA = code.getParameter(0, Type.INT);
+        Local<Integer> localB = code.getParameter(1, Type.INT);
+        Local<Boolean> result = code.newLocal(Type.get(boolean.class));
         Label afterIf = code.newLabel();
         Label ifBody = code.newLabel();
         code.compare(comparison, localA, localB, ifBody);
@@ -494,53 +476,53 @@
     }
 
     public void testCastIntegerToInteger() throws Exception {
-        java.lang.reflect.Method intToLong = numericCastingMethod(int.class, long.class);
+        Method intToLong = numericCastingMethod(int.class, long.class);
         assertEquals(0x0000000000000000L, intToLong.invoke(null, 0x00000000));
         assertEquals(0x000000007fffffffL, intToLong.invoke(null, 0x7fffffff));
         assertEquals(0xffffffff80000000L, intToLong.invoke(null, 0x80000000));
         assertEquals(0xffffffffffffffffL, intToLong.invoke(null, 0xffffffff));
 
-        java.lang.reflect.Method longToInt = numericCastingMethod(long.class, int.class);
+        Method longToInt = numericCastingMethod(long.class, int.class);
         assertEquals(0x1234abcd, longToInt.invoke(null, 0x000000001234abcdL));
         assertEquals(0x1234abcd, longToInt.invoke(null, 0x123456781234abcdL));
         assertEquals(0x1234abcd, longToInt.invoke(null, 0xffffffff1234abcdL));
 
-        java.lang.reflect.Method intToShort = numericCastingMethod(int.class, short.class);
+        Method intToShort = numericCastingMethod(int.class, short.class);
         assertEquals((short) 0x1234, intToShort.invoke(null, 0x00001234));
         assertEquals((short) 0x1234, intToShort.invoke(null, 0xabcd1234));
         assertEquals((short) 0x1234, intToShort.invoke(null, 0xffff1234));
 
-        java.lang.reflect.Method intToChar = numericCastingMethod(int.class, char.class);
+        Method intToChar = numericCastingMethod(int.class, char.class);
         assertEquals((char) 0x1234, intToChar.invoke(null, 0x00001234));
         assertEquals((char) 0x1234, intToChar.invoke(null, 0xabcd1234));
         assertEquals((char) 0x1234, intToChar.invoke(null, 0xffff1234));
 
-        java.lang.reflect.Method intToByte = numericCastingMethod(int.class, byte.class);
+        Method intToByte = numericCastingMethod(int.class, byte.class);
         assertEquals((byte) 0x34, intToByte.invoke(null, 0x00000034));
         assertEquals((byte) 0x34, intToByte.invoke(null, 0xabcd1234));
         assertEquals((byte) 0x34, intToByte.invoke(null, 0xffffff34));
     }
 
     public void testCastIntegerToFloatingPoint() throws Exception {
-        java.lang.reflect.Method intToFloat = numericCastingMethod(int.class, float.class);
+        Method intToFloat = numericCastingMethod(int.class, float.class);
         assertEquals(0.0f, intToFloat.invoke(null, 0));
         assertEquals(-1.0f, intToFloat.invoke(null, -1));
         assertEquals(16777216f, intToFloat.invoke(null, 16777216));
         assertEquals(16777216f, intToFloat.invoke(null, 16777217)); // precision
 
-        java.lang.reflect.Method intToDouble = numericCastingMethod(int.class, double.class);
+        Method intToDouble = numericCastingMethod(int.class, double.class);
         assertEquals(0.0, intToDouble.invoke(null, 0));
         assertEquals(-1.0, intToDouble.invoke(null, -1));
         assertEquals(16777216.0, intToDouble.invoke(null, 16777216));
         assertEquals(16777217.0, intToDouble.invoke(null, 16777217));
 
-        java.lang.reflect.Method longToFloat = numericCastingMethod(long.class, float.class);
+        Method longToFloat = numericCastingMethod(long.class, float.class);
         assertEquals(0.0f, longToFloat.invoke(null, 0L));
         assertEquals(-1.0f, longToFloat.invoke(null, -1L));
         assertEquals(16777216f, longToFloat.invoke(null, 16777216L));
         assertEquals(16777216f, longToFloat.invoke(null, 16777217L));
 
-        java.lang.reflect.Method longToDouble = numericCastingMethod(long.class, double.class);
+        Method longToDouble = numericCastingMethod(long.class, double.class);
         assertEquals(0.0, longToDouble.invoke(null, 0L));
         assertEquals(-1.0, longToDouble.invoke(null, -1L));
         assertEquals(9007199254740992.0, longToDouble.invoke(null, 9007199254740992L));
@@ -548,7 +530,7 @@
     }
 
     public void testCastFloatingPointToInteger() throws Exception {
-        java.lang.reflect.Method floatToInt = numericCastingMethod(float.class, int.class);
+        Method floatToInt = numericCastingMethod(float.class, int.class);
         assertEquals(0, floatToInt.invoke(null, 0.0f));
         assertEquals(-1, floatToInt.invoke(null, -1.0f));
         assertEquals(Integer.MAX_VALUE, floatToInt.invoke(null, 10e15f));
@@ -556,7 +538,7 @@
         assertEquals(Integer.MIN_VALUE, floatToInt.invoke(null, Float.NEGATIVE_INFINITY));
         assertEquals(0, floatToInt.invoke(null, Float.NaN));
 
-        java.lang.reflect.Method floatToLong = numericCastingMethod(float.class, long.class);
+        Method floatToLong = numericCastingMethod(float.class, long.class);
         assertEquals(0L, floatToLong.invoke(null, 0.0f));
         assertEquals(-1L, floatToLong.invoke(null, -1.0f));
         assertEquals(10000000272564224L, floatToLong.invoke(null, 10e15f));
@@ -564,7 +546,7 @@
         assertEquals(Long.MIN_VALUE, floatToLong.invoke(null, Float.NEGATIVE_INFINITY));
         assertEquals(0L, floatToLong.invoke(null, Float.NaN));
 
-        java.lang.reflect.Method doubleToInt = numericCastingMethod(double.class, int.class);
+        Method doubleToInt = numericCastingMethod(double.class, int.class);
         assertEquals(0, doubleToInt.invoke(null, 0.0));
         assertEquals(-1, doubleToInt.invoke(null, -1.0));
         assertEquals(Integer.MAX_VALUE, doubleToInt.invoke(null, 10e15));
@@ -572,7 +554,7 @@
         assertEquals(Integer.MIN_VALUE, doubleToInt.invoke(null, Double.NEGATIVE_INFINITY));
         assertEquals(0, doubleToInt.invoke(null, Double.NaN));
 
-        java.lang.reflect.Method doubleToLong = numericCastingMethod(double.class, long.class);
+        Method doubleToLong = numericCastingMethod(double.class, long.class);
         assertEquals(0L, doubleToLong.invoke(null, 0.0));
         assertEquals(-1L, doubleToLong.invoke(null, -1.0));
         assertEquals(10000000000000000L, doubleToLong.invoke(null, 10e15));
@@ -582,14 +564,14 @@
     }
 
     public void testCastFloatingPointToFloatingPoint() throws Exception {
-        java.lang.reflect.Method floatToDouble = numericCastingMethod(float.class, double.class);
+        Method floatToDouble = numericCastingMethod(float.class, double.class);
         assertEquals(0.0, floatToDouble.invoke(null, 0.0f));
         assertEquals(-1.0, floatToDouble.invoke(null, -1.0f));
         assertEquals(0.5, floatToDouble.invoke(null, 0.5f));
         assertEquals(Double.NEGATIVE_INFINITY, floatToDouble.invoke(null, Float.NEGATIVE_INFINITY));
         assertEquals(Double.NaN, floatToDouble.invoke(null, Float.NaN));
 
-        java.lang.reflect.Method doubleToFloat = numericCastingMethod(double.class, float.class);
+        Method doubleToFloat = numericCastingMethod(double.class, float.class);
         assertEquals(0.0f, doubleToFloat.invoke(null, 0.0));
         assertEquals(-1.0f, doubleToFloat.invoke(null, -1.0));
         assertEquals(0.5f, doubleToFloat.invoke(null, 0.5));
@@ -597,7 +579,7 @@
         assertEquals(Float.NaN, doubleToFloat.invoke(null, Double.NaN));
     }
 
-    private java.lang.reflect.Method numericCastingMethod(Class<?> source, Class<?> target)
+    private Method numericCastingMethod(Class<?> source, Class<?> target)
             throws Exception {
         /*
          * public static short call(int source) {
@@ -606,10 +588,10 @@
          * }
          */
         reset();
-        Type<?> sourceType = generator.getType(source);
-        Type<?> targetType = generator.getType(target);
-        Code code = generatedType.getMethod(targetType, "call", sourceType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        Type<?> sourceType = Type.get(source);
+        Type<?> targetType = Type.get(target);
+        MethodId<?, ?> methodId = GENERATED.getMethod(targetType, "call", sourceType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<?> localSource = code.getParameter(0, sourceType);
         Local<?> localCasted = code.newLocal(targetType);
         code.numericCast(localSource, localCasted);
@@ -618,18 +600,18 @@
     }
 
     public void testNot() throws Exception {
-        java.lang.reflect.Method notInteger = notMethod(int.class);
+        Method notInteger = notMethod(int.class);
         assertEquals(0xffffffff, notInteger.invoke(null, 0x00000000));
         assertEquals(0x00000000, notInteger.invoke(null, 0xffffffff));
         assertEquals(0xedcba987, notInteger.invoke(null, 0x12345678));
 
-        java.lang.reflect.Method notLong = notMethod(long.class);
+        Method notLong = notMethod(long.class);
         assertEquals(0xffffffffffffffffL, notLong.invoke(null, 0x0000000000000000L));
         assertEquals(0x0000000000000000L, notLong.invoke(null, 0xffffffffffffffffL));
         assertEquals(0x98765432edcba987L, notLong.invoke(null, 0x6789abcd12345678L));
     }
 
-    private <T> java.lang.reflect.Method notMethod(Class<T> source) throws Exception {
+    private <T> Method notMethod(Class<T> source) throws Exception {
         /*
          * public static short call(int source) {
          *   source = ~source;
@@ -637,9 +619,9 @@
          * }
          */
         reset();
-        Type<T> valueType = generator.getType(source);
-        Code code = generatedType.getMethod(valueType, "call", valueType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        Type<T> valueType = Type.get(source);
+        MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<T> localSource = code.getParameter(0, valueType);
         code.not(localSource, localSource);
         code.returnValue(localSource);
@@ -647,30 +629,30 @@
     }
 
     public void testNegate() throws Exception {
-        java.lang.reflect.Method negateInteger = negateMethod(int.class);
+        Method negateInteger = negateMethod(int.class);
         assertEquals(0, negateInteger.invoke(null, 0));
         assertEquals(-1, negateInteger.invoke(null, 1));
         assertEquals(Integer.MIN_VALUE, negateInteger.invoke(null, Integer.MIN_VALUE));
 
-        java.lang.reflect.Method negateLong = negateMethod(long.class);
+        Method negateLong = negateMethod(long.class);
         assertEquals(0L, negateLong.invoke(null, 0));
         assertEquals(-1L, negateLong.invoke(null, 1));
         assertEquals(Long.MIN_VALUE, negateLong.invoke(null, Long.MIN_VALUE));
 
-        java.lang.reflect.Method negateFloat = negateMethod(float.class);
+        Method negateFloat = negateMethod(float.class);
         assertEquals(-0.0f, negateFloat.invoke(null, 0.0f));
         assertEquals(-1.0f, negateFloat.invoke(null, 1.0f));
         assertEquals(Float.NaN, negateFloat.invoke(null, Float.NaN));
         assertEquals(Float.POSITIVE_INFINITY, negateFloat.invoke(null, Float.NEGATIVE_INFINITY));
 
-        java.lang.reflect.Method negateDouble = negateMethod(double.class);
+        Method negateDouble = negateMethod(double.class);
         assertEquals(-0.0, negateDouble.invoke(null, 0.0));
         assertEquals(-1.0, negateDouble.invoke(null, 1.0));
         assertEquals(Double.NaN, negateDouble.invoke(null, Double.NaN));
         assertEquals(Double.POSITIVE_INFINITY, negateDouble.invoke(null, Double.NEGATIVE_INFINITY));
     }
 
-    private <T> java.lang.reflect.Method negateMethod(Class<T> source) throws Exception {
+    private <T> Method negateMethod(Class<T> source) throws Exception {
         /*
          * public static short call(int source) {
          *   source = -source;
@@ -678,9 +660,9 @@
          * }
          */
         reset();
-        Type<T> valueType = generator.getType(source);
-        Code code = generatedType.getMethod(valueType, "call", valueType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        Type<T> valueType = Type.get(source);
+        MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<T> localSource = code.getParameter(0, valueType);
         code.negate(localSource, localSource);
         code.returnValue(localSource);
@@ -688,16 +670,16 @@
     }
 
     public void testIntBinaryOps() throws Exception {
-        java.lang.reflect.Method add = binaryOpMethod(int.class, BinaryOp.ADD);
+        Method add = binaryOpMethod(int.class, BinaryOp.ADD);
         assertEquals(79, add.invoke(null, 75, 4));
 
-        java.lang.reflect.Method subtract = binaryOpMethod(int.class, BinaryOp.SUBTRACT);
+        Method subtract = binaryOpMethod(int.class, BinaryOp.SUBTRACT);
         assertEquals(71, subtract.invoke(null, 75, 4));
 
-        java.lang.reflect.Method multiply = binaryOpMethod(int.class, BinaryOp.MULTIPLY);
+        Method multiply = binaryOpMethod(int.class, BinaryOp.MULTIPLY);
         assertEquals(300, multiply.invoke(null, 75, 4));
 
-        java.lang.reflect.Method divide = binaryOpMethod(int.class, BinaryOp.DIVIDE);
+        Method divide = binaryOpMethod(int.class, BinaryOp.DIVIDE);
         assertEquals(18, divide.invoke(null, 75, 4));
         try {
             divide.invoke(null, 75, 0);
@@ -706,7 +688,7 @@
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        java.lang.reflect.Method remainder = binaryOpMethod(int.class, BinaryOp.REMAINDER);
+        Method remainder = binaryOpMethod(int.class, BinaryOp.REMAINDER);
         assertEquals(3, remainder.invoke(null, 75, 4));
         try {
             remainder.invoke(null, 75, 0);
@@ -715,37 +697,37 @@
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        java.lang.reflect.Method and = binaryOpMethod(int.class, BinaryOp.AND);
+        Method and = binaryOpMethod(int.class, BinaryOp.AND);
         assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000));
 
-        java.lang.reflect.Method or = binaryOpMethod(int.class, BinaryOp.OR);
+        Method or = binaryOpMethod(int.class, BinaryOp.OR);
         assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000));
 
-        java.lang.reflect.Method xor = binaryOpMethod(int.class, BinaryOp.XOR);
+        Method xor = binaryOpMethod(int.class, BinaryOp.XOR);
         assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000));
 
-        java.lang.reflect.Method shiftLeft = binaryOpMethod(int.class, BinaryOp.SHIFT_LEFT);
+        Method shiftLeft = binaryOpMethod(int.class, BinaryOp.SHIFT_LEFT);
         assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8));
 
-        java.lang.reflect.Method shiftRight = binaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT);
+        Method shiftRight = binaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT);
         assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8));
 
-        java.lang.reflect.Method unsignedShiftRight = binaryOpMethod(int.class,
+        Method unsignedShiftRight = binaryOpMethod(int.class,
                 BinaryOp.UNSIGNED_SHIFT_RIGHT);
         assertEquals(0x00abcd12, unsignedShiftRight.invoke(null, 0xabcd1234, 8));
     }
 
     public void testLongBinaryOps() throws Exception {
-        java.lang.reflect.Method add = binaryOpMethod(long.class, BinaryOp.ADD);
+        Method add = binaryOpMethod(long.class, BinaryOp.ADD);
         assertEquals(79L, add.invoke(null, 75L, 4L));
 
-        java.lang.reflect.Method subtract = binaryOpMethod(long.class, BinaryOp.SUBTRACT);
+        Method subtract = binaryOpMethod(long.class, BinaryOp.SUBTRACT);
         assertEquals(71L, subtract.invoke(null, 75L, 4L));
 
-        java.lang.reflect.Method multiply = binaryOpMethod(long.class, BinaryOp.MULTIPLY);
+        Method multiply = binaryOpMethod(long.class, BinaryOp.MULTIPLY);
         assertEquals(300L, multiply.invoke(null, 75L, 4L));
 
-        java.lang.reflect.Method divide = binaryOpMethod(long.class, BinaryOp.DIVIDE);
+        Method divide = binaryOpMethod(long.class, BinaryOp.DIVIDE);
         assertEquals(18L, divide.invoke(null, 75L, 4L));
         try {
             divide.invoke(null, 75L, 0L);
@@ -754,7 +736,7 @@
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        java.lang.reflect.Method remainder = binaryOpMethod(long.class, BinaryOp.REMAINDER);
+        Method remainder = binaryOpMethod(long.class, BinaryOp.REMAINDER);
         assertEquals(3L, remainder.invoke(null, 75L, 4L));
         try {
             remainder.invoke(null, 75L, 0L);
@@ -763,67 +745,68 @@
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        java.lang.reflect.Method and = binaryOpMethod(long.class, BinaryOp.AND);
+        Method and = binaryOpMethod(long.class, BinaryOp.AND);
         assertEquals(0xff00ff0000000000L,
                 and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
 
-        java.lang.reflect.Method or = binaryOpMethod(long.class, BinaryOp.OR);
-        assertEquals(0xffffffffff00ff00L, or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
+        Method or = binaryOpMethod(long.class, BinaryOp.OR);
+        assertEquals(0xffffffffff00ff00L,
+                or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
 
-        java.lang.reflect.Method xor = binaryOpMethod(long.class, BinaryOp.XOR);
+        Method xor = binaryOpMethod(long.class, BinaryOp.XOR);
         assertEquals(0x00ff00ffff00ff00L,
                 xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
 
-        java.lang.reflect.Method shiftLeft = binaryOpMethod(long.class, BinaryOp.SHIFT_LEFT);
+        Method shiftLeft = binaryOpMethod(long.class, BinaryOp.SHIFT_LEFT);
         assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8L));
 
-        java.lang.reflect.Method shiftRight = binaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT);
+        Method shiftRight = binaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT);
         assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8L));
 
-        java.lang.reflect.Method unsignedShiftRight = binaryOpMethod(long.class,
+        Method unsignedShiftRight = binaryOpMethod(long.class,
                 BinaryOp.UNSIGNED_SHIFT_RIGHT);
         assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8L));
     }
 
     public void testFloatBinaryOps() throws Exception {
-        java.lang.reflect.Method add = binaryOpMethod(float.class, BinaryOp.ADD);
+        Method add = binaryOpMethod(float.class, BinaryOp.ADD);
         assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f));
 
-        java.lang.reflect.Method subtract = binaryOpMethod(float.class, BinaryOp.SUBTRACT);
+        Method subtract = binaryOpMethod(float.class, BinaryOp.SUBTRACT);
         assertEquals(4.25f, subtract.invoke(null, 5.5f, 1.25f));
 
-        java.lang.reflect.Method multiply = binaryOpMethod(float.class, BinaryOp.MULTIPLY);
+        Method multiply = binaryOpMethod(float.class, BinaryOp.MULTIPLY);
         assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f));
 
-        java.lang.reflect.Method divide = binaryOpMethod(float.class, BinaryOp.DIVIDE);
+        Method divide = binaryOpMethod(float.class, BinaryOp.DIVIDE);
         assertEquals(4.4f, divide.invoke(null, 5.5f, 1.25f));
         assertEquals(Float.POSITIVE_INFINITY, divide.invoke(null, 5.5f, 0.0f));
 
-        java.lang.reflect.Method remainder = binaryOpMethod(float.class, BinaryOp.REMAINDER);
+        Method remainder = binaryOpMethod(float.class, BinaryOp.REMAINDER);
         assertEquals(0.5f, remainder.invoke(null, 5.5f, 1.25f));
         assertEquals(Float.NaN, remainder.invoke(null, 5.5f, 0.0f));
     }
 
     public void testDoubleBinaryOps() throws Exception {
-        java.lang.reflect.Method add = binaryOpMethod(double.class, BinaryOp.ADD);
+        Method add = binaryOpMethod(double.class, BinaryOp.ADD);
         assertEquals(6.75, add.invoke(null, 5.5, 1.25));
 
-        java.lang.reflect.Method subtract = binaryOpMethod(double.class, BinaryOp.SUBTRACT);
+        Method subtract = binaryOpMethod(double.class, BinaryOp.SUBTRACT);
         assertEquals(4.25, subtract.invoke(null, 5.5, 1.25));
 
-        java.lang.reflect.Method multiply = binaryOpMethod(double.class, BinaryOp.MULTIPLY);
+        Method multiply = binaryOpMethod(double.class, BinaryOp.MULTIPLY);
         assertEquals(6.875, multiply.invoke(null, 5.5, 1.25));
 
-        java.lang.reflect.Method divide = binaryOpMethod(double.class, BinaryOp.DIVIDE);
+        Method divide = binaryOpMethod(double.class, BinaryOp.DIVIDE);
         assertEquals(4.4, divide.invoke(null, 5.5, 1.25));
         assertEquals(Double.POSITIVE_INFINITY, divide.invoke(null, 5.5, 0.0));
 
-        java.lang.reflect.Method remainder = binaryOpMethod(double.class, BinaryOp.REMAINDER);
+        Method remainder = binaryOpMethod(double.class, BinaryOp.REMAINDER);
         assertEquals(0.5, remainder.invoke(null, 5.5, 1.25));
         assertEquals(Double.NaN, remainder.invoke(null, 5.5, 0.0));
     }
 
-    private <T> java.lang.reflect.Method binaryOpMethod(Class<T> valueClass, BinaryOp op)
+    private <T> Method binaryOpMethod(Class<T> valueClass, BinaryOp op)
             throws Exception {
         /*
          * public static int binaryOp(int a, int b) {
@@ -832,9 +815,9 @@
          * }
          */
         reset();
-        Type<T> valueType = generator.getType(valueClass);
-        Code code = generatedType.getMethod(valueType, "call", valueType,valueType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        Type<T> valueType = Type.get(valueClass);
+        MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType, valueType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<T> localA = code.getParameter(0, valueType);
         Local<T> localB = code.getParameter(1, valueType);
         Local<T> localResult = code.newLocal(valueType);
@@ -846,47 +829,47 @@
     public void testReadAndWriteInstanceFields() throws Exception {
         Instance instance = new Instance();
 
-        java.lang.reflect.Method intSwap = instanceSwapMethod(int.class, "intValue");
+        Method intSwap = instanceSwapMethod(int.class, "intValue");
         instance.intValue = 5;
         assertEquals(5, intSwap.invoke(null, instance, 10));
         assertEquals(10, instance.intValue);
 
-        java.lang.reflect.Method longSwap = instanceSwapMethod(long.class, "longValue");
+        Method longSwap = instanceSwapMethod(long.class, "longValue");
         instance.longValue = 500L;
         assertEquals(500L, longSwap.invoke(null, instance, 1234L));
         assertEquals(1234L, instance.longValue);
 
-        java.lang.reflect.Method booleanSwap = instanceSwapMethod(boolean.class, "booleanValue");
+        Method booleanSwap = instanceSwapMethod(boolean.class, "booleanValue");
         instance.booleanValue = false;
         assertEquals(false, booleanSwap.invoke(null, instance, true));
         assertEquals(true, instance.booleanValue);
 
-        java.lang.reflect.Method floatSwap = instanceSwapMethod(float.class, "floatValue");
+        Method floatSwap = instanceSwapMethod(float.class, "floatValue");
         instance.floatValue = 1.5f;
         assertEquals(1.5f, floatSwap.invoke(null, instance, 0.5f));
         assertEquals(0.5f, instance.floatValue);
 
-        java.lang.reflect.Method doubleSwap = instanceSwapMethod(double.class, "doubleValue");
+        Method doubleSwap = instanceSwapMethod(double.class, "doubleValue");
         instance.doubleValue = 155.5;
         assertEquals(155.5, doubleSwap.invoke(null, instance, 266.6));
         assertEquals(266.6, instance.doubleValue);
 
-        java.lang.reflect.Method objectSwap = instanceSwapMethod(Object.class, "objectValue");
+        Method objectSwap = instanceSwapMethod(Object.class, "objectValue");
         instance.objectValue = "before";
         assertEquals("before", objectSwap.invoke(null, instance, "after"));
         assertEquals("after", instance.objectValue);
 
-        java.lang.reflect.Method byteSwap = instanceSwapMethod(byte.class, "byteValue");
+        Method byteSwap = instanceSwapMethod(byte.class, "byteValue");
         instance.byteValue = 0x35;
         assertEquals((byte) 0x35, byteSwap.invoke(null, instance, (byte) 0x64));
         assertEquals((byte) 0x64, instance.byteValue);
 
-        java.lang.reflect.Method charSwap = instanceSwapMethod(char.class, "charValue");
+        Method charSwap = instanceSwapMethod(char.class, "charValue");
         instance.charValue = 'A';
         assertEquals('A', charSwap.invoke(null, instance, 'B'));
         assertEquals('B', instance.charValue);
 
-        java.lang.reflect.Method shortSwap = instanceSwapMethod(short.class, "shortValue");
+        Method shortSwap = instanceSwapMethod(short.class, "shortValue");
         instance.shortValue = (short) 0xabcd;
         assertEquals((short) 0xabcd, shortSwap.invoke(null, instance, (short) 0x1234));
         assertEquals((short) 0x1234, instance.shortValue);
@@ -904,7 +887,7 @@
         public short shortValue;
     }
 
-    private <V> java.lang.reflect.Method instanceSwapMethod(
+    private <V> Method instanceSwapMethod(
             Class<V> valueClass, String fieldName) throws Exception {
         /*
          * public static int call(Instance instance, int newValue) {
@@ -914,62 +897,62 @@
          * }
          */
         reset();
-        Type<V> valueType = generator.getType(valueClass);
-        Type<Instance> objectType = generator.getType(Instance.class);
-        Field<Instance, V> field = objectType.getField(valueType, fieldName);
-        Code code = generatedType.getMethod(valueType, "call", objectType, valueType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        Type<V> valueType = Type.get(valueClass);
+        Type<Instance> objectType = Type.get(Instance.class);
+        FieldId<Instance, V> fieldId = objectType.getField(valueType, fieldName);
+        MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", objectType, valueType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<Instance> localInstance = code.getParameter(0, objectType);
         Local<V> localNewValue = code.getParameter(1, valueType);
         Local<V> localOldValue = code.newLocal(valueType);
-        code.iget(field, localInstance, localOldValue);
-        code.iput(field, localInstance, localNewValue);
+        code.iget(fieldId, localInstance, localOldValue);
+        code.iput(fieldId, localInstance, localNewValue);
         code.returnValue(localOldValue);
         return getMethod();
     }
 
     public void testReadAndWriteStaticFields() throws Exception {
-        java.lang.reflect.Method intSwap = staticSwapMethod(int.class, "intValue");
+        Method intSwap = staticSwapMethod(int.class, "intValue");
         Static.intValue = 5;
         assertEquals(5, intSwap.invoke(null, 10));
         assertEquals(10, Static.intValue);
 
-        java.lang.reflect.Method longSwap = staticSwapMethod(long.class, "longValue");
+        Method longSwap = staticSwapMethod(long.class, "longValue");
         Static.longValue = 500L;
         assertEquals(500L, longSwap.invoke(null, 1234L));
         assertEquals(1234L, Static.longValue);
 
-        java.lang.reflect.Method booleanSwap = staticSwapMethod(boolean.class, "booleanValue");
+        Method booleanSwap = staticSwapMethod(boolean.class, "booleanValue");
         Static.booleanValue = false;
         assertEquals(false, booleanSwap.invoke(null, true));
         assertEquals(true, Static.booleanValue);
 
-        java.lang.reflect.Method floatSwap = staticSwapMethod(float.class, "floatValue");
+        Method floatSwap = staticSwapMethod(float.class, "floatValue");
         Static.floatValue = 1.5f;
         assertEquals(1.5f, floatSwap.invoke(null, 0.5f));
         assertEquals(0.5f, Static.floatValue);
 
-        java.lang.reflect.Method doubleSwap = staticSwapMethod(double.class, "doubleValue");
+        Method doubleSwap = staticSwapMethod(double.class, "doubleValue");
         Static.doubleValue = 155.5;
         assertEquals(155.5, doubleSwap.invoke(null, 266.6));
         assertEquals(266.6, Static.doubleValue);
 
-        java.lang.reflect.Method objectSwap = staticSwapMethod(Object.class, "objectValue");
+        Method objectSwap = staticSwapMethod(Object.class, "objectValue");
         Static.objectValue = "before";
         assertEquals("before", objectSwap.invoke(null, "after"));
         assertEquals("after", Static.objectValue);
 
-        java.lang.reflect.Method byteSwap = staticSwapMethod(byte.class, "byteValue");
+        Method byteSwap = staticSwapMethod(byte.class, "byteValue");
         Static.byteValue = 0x35;
         assertEquals((byte) 0x35, byteSwap.invoke(null, (byte) 0x64));
         assertEquals((byte) 0x64, Static.byteValue);
 
-        java.lang.reflect.Method charSwap = staticSwapMethod(char.class, "charValue");
+        Method charSwap = staticSwapMethod(char.class, "charValue");
         Static.charValue = 'A';
         assertEquals('A', charSwap.invoke(null, 'B'));
         assertEquals('B', Static.charValue);
 
-        java.lang.reflect.Method shortSwap = staticSwapMethod(short.class, "shortValue");
+        Method shortSwap = staticSwapMethod(short.class, "shortValue");
         Static.shortValue = (short) 0xabcd;
         assertEquals((short) 0xabcd, shortSwap.invoke(null, (short) 0x1234));
         assertEquals((short) 0x1234, Static.shortValue);
@@ -987,7 +970,7 @@
         public static short shortValue;
     }
 
-    private <V> java.lang.reflect.Method staticSwapMethod(Class<V> valueClass, String fieldName)
+    private <V> Method staticSwapMethod(Class<V> valueClass, String fieldName)
             throws Exception {
         /*
          * public static int call(int newValue) {
@@ -997,15 +980,15 @@
          * }
          */
         reset();
-        Type<V> valueType = generator.getType(valueClass);
-        Type<Static> objectType = generator.getType(Static.class);
-        Field<Static, V> field = objectType.getField(valueType, fieldName);
-        Code code = generatedType.getMethod(valueType, "call", valueType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        Type<V> valueType = Type.get(valueClass);
+        Type<Static> objectType = Type.get(Static.class);
+        FieldId<Static, V> fieldId = objectType.getField(valueType, fieldName);
+        MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", valueType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<V> localNewValue = code.getParameter(0, valueType);
         Local<V> localOldValue = code.newLocal(valueType);
-        code.sget(field, localOldValue);
-        code.sput(field, localNewValue);
+        code.sget(fieldId, localOldValue);
+        code.sput(fieldId, localNewValue);
         code.returnValue(localOldValue);
         return getMethod();
     }
@@ -1016,14 +999,14 @@
          *   String s = (String) o;
          * }
          */
-        Code code = generatedType.getMethod(stringType, "call", objectType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Object> localObject = code.getParameter(0, objectType);
-        Local<String> localString = code.newLocal(stringType);
+        MethodId<?, String> methodId = GENERATED.getMethod(Type.STRING, "call", Type.OBJECT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Object> localObject = code.getParameter(0, Type.OBJECT);
+        Local<String> localString = code.newLocal(Type.STRING);
         code.typeCast(localObject, localString);
         code.returnValue(localString);
 
-        java.lang.reflect.Method method = getMethod();
+        Method method = getMethod();
         assertEquals("s", method.invoke(null, "s"));
         assertEquals(null, method.invoke(null, (String) null));
         try {
@@ -1041,14 +1024,14 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(booleanType, "call", objectType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Object> localObject = code.getParameter(0, objectType);
-        Local<Boolean> localResult = code.newLocal(booleanType);
-        code.instanceOfType(localResult, localObject, stringType);
+        MethodId<?, Boolean> methodId = GENERATED.getMethod(Type.BOOLEAN, "call", Type.OBJECT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Object> localObject = code.getParameter(0, Type.OBJECT);
+        Local<Boolean> localResult = code.newLocal(Type.BOOLEAN);
+        code.instanceOfType(localResult, localObject, Type.STRING);
         code.returnValue(localResult);
 
-        java.lang.reflect.Method method = getMethod();
+        Method method = getMethod();
         assertEquals(true, method.invoke(null, "s"));
         assertEquals(false, method.invoke(null, (String) null));
         assertEquals(false, method.invoke(null, 5));
@@ -1067,13 +1050,13 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(intType, "call", intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localCount = code.getParameter(0, intType);
-        Local<Integer> localResult = code.newLocal(intType);
-        Local<Integer> localI = code.newLocal(intType);
-        Local<Integer> local1 = code.newLocal(intType);
-        Local<Integer> local2 = code.newLocal(intType);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localCount = code.getParameter(0, Type.INT);
+        Local<Integer> localResult = code.newLocal(Type.INT);
+        Local<Integer> localI = code.newLocal(Type.INT);
+        Local<Integer> local1 = code.newLocal(Type.INT);
+        Local<Integer> local2 = code.newLocal(Type.INT);
         code.loadConstant(local1, 1);
         code.loadConstant(local2, 2);
         code.loadConstant(localResult, 1);
@@ -1091,7 +1074,7 @@
         code.mark(afterLoop);
         code.returnValue(localResult);
 
-        java.lang.reflect.Method pow2 = getMethod();
+        Method pow2 = getMethod();
         assertEquals(1, pow2.invoke(null, 0));
         assertEquals(2, pow2.invoke(null, 1));
         assertEquals(4, pow2.invoke(null, 2));
@@ -1112,11 +1095,11 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(intType, "call", intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localMax = code.getParameter(0, intType);
-        Local<Integer> localResult = code.newLocal(intType);
-        Local<Integer> local2 = code.newLocal(intType);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localMax = code.getParameter(0, Type.INT);
+        Local<Integer> localResult = code.newLocal(Type.INT);
+        Local<Integer> local2 = code.newLocal(Type.INT);
         code.loadConstant(localResult, 1);
         code.loadConstant(local2, 2);
         Label loopCondition = code.newLabel();
@@ -1131,7 +1114,7 @@
         code.mark(afterLoop);
         code.returnValue(localResult);
 
-        java.lang.reflect.Method ceilPow2 = getMethod();
+        Method ceilPow2 = getMethod();
         assertEquals(1, ceilPow2.invoke(null, 1));
         assertEquals(2, ceilPow2.invoke(null, 2));
         assertEquals(4, ceilPow2.invoke(null, 3));
@@ -1156,11 +1139,12 @@
          *   }
          * }
          */
-        Code code = generatedType.getMethod(intType, "call", intType, intType, intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localA = code.getParameter(0, intType);
-        Local<Integer> localB = code.getParameter(1, intType);
-        Local<Integer> localC = code.getParameter(2, intType);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(
+                Type.INT, "call", Type.INT, Type.INT, Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localA = code.getParameter(0, Type.INT);
+        Local<Integer> localB = code.getParameter(1, Type.INT);
+        Local<Integer> localC = code.getParameter(2, Type.INT);
         Label aLessThanB = code.newLabel();
         Label aLessThanC = code.newLabel();
         Label bLessThanC = code.newLabel();
@@ -1178,7 +1162,7 @@
         code.mark(bLessThanC);
         code.returnValue(localB);
 
-        java.lang.reflect.Method min = getMethod();
+        Method min = getMethod();
         assertEquals(1, min.invoke(null, 1, 2, 3));
         assertEquals(1, min.invoke(null, 2, 3, 1));
         assertEquals(1, min.invoke(null, 2, 1, 3));
@@ -1199,28 +1183,28 @@
          *   return result;
          * }
          */
-        Method<?, Integer> c = generatedType.getMethod(intType, "call", intType);
-        Code code = c.declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localA = code.getParameter(0, intType);
-        Local<Integer> local1 = code.newLocal(intType);
-        Local<Integer> local2 = code.newLocal(intType);
-        Local<Integer> localX = code.newLocal(intType);
-        Local<Integer> localY = code.newLocal(intType);
-        Local<Integer> localResult = code.newLocal(intType);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localA = code.getParameter(0, Type.INT);
+        Local<Integer> local1 = code.newLocal(Type.INT);
+        Local<Integer> local2 = code.newLocal(Type.INT);
+        Local<Integer> localX = code.newLocal(Type.INT);
+        Local<Integer> localY = code.newLocal(Type.INT);
+        Local<Integer> localResult = code.newLocal(Type.INT);
         Label baseCase = code.newLabel();
         code.loadConstant(local1, 1);
         code.loadConstant(local2, 2);
         code.compare(Comparison.LT, localA, local2, baseCase);
         code.op(BinaryOp.SUBTRACT, localA, localA, local1);
-        code.invokeStatic(c, localX, localA);
+        code.invokeStatic(methodId, localX, localA);
         code.op(BinaryOp.SUBTRACT, localA, localA, local1);
-        code.invokeStatic(c, localY, localA);
+        code.invokeStatic(methodId, localY, localA);
         code.op(BinaryOp.ADD, localResult, localX, localY);
         code.returnValue(localResult);
         code.mark(baseCase);
         code.returnValue(localA);
 
-        java.lang.reflect.Method fib = getMethod();
+        Method fib = getMethod();
         assertEquals(0, fib.invoke(null, 0));
         assertEquals(1, fib.invoke(null, 1));
         assertEquals(1, fib.invoke(null, 2));
@@ -1244,18 +1228,18 @@
          *     return "RE";
          *   }
          */
-        Code code = generatedType.getMethod(stringType, "call", intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localI = code.getParameter(0, intType);
-        Local<String> result = code.newLocal(stringType);
+        MethodId<?, String> methodId = GENERATED.getMethod(Type.STRING, "call", Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localI = code.getParameter(0, Type.INT);
+        Local<String> result = code.newLocal(Type.STRING);
         Label catchIae = code.newLabel();
         Label catchIse = code.newLabel();
         Label catchRe = code.newLabel();
 
-        code.addCatchClause(generator.getType(IllegalArgumentException.class), catchIae);
-        code.addCatchClause(generator.getType(IllegalStateException.class), catchIse);
-        code.addCatchClause(generator.getType(RuntimeException.class), catchRe);
-        Method<?, ?> thrower = dexGeneratorTestType.getMethod(voidType, "thrower", intType);
+        code.addCatchClause(Type.get(IllegalArgumentException.class), catchIae);
+        code.addCatchClause(Type.get(IllegalStateException.class), catchIse);
+        code.addCatchClause(Type.get(RuntimeException.class), catchRe);
+        MethodId<?, ?> thrower = TEST_TYPE.getMethod(Type.VOID, "thrower", Type.INT);
         code.invokeStatic(thrower, null, localI);
         code.loadConstant(result, "NONE");
         code.returnValue(result);
@@ -1272,7 +1256,7 @@
         code.loadConstant(result, "RE");
         code.returnValue(result);
 
-        java.lang.reflect.Method method = getMethod();
+        Method method = getMethod();
         assertEquals("NONE", method.invoke(null, 0));
         assertEquals("IAE", method.invoke(null, 1));
         assertEquals("ISE", method.invoke(null, 2));
@@ -1319,19 +1303,20 @@
          *     return "OUTER";
          *   }
          */
-        Code code = generatedType.getMethod(stringType, "call", intType, intType, intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localA = code.getParameter(0, intType);
-        Local<Integer> localB = code.getParameter(1, intType);
-        Local<Integer> localC = code.getParameter(2, intType);
-        Local<String> localResult = code.newLocal(stringType);
+        MethodId<?, String> methodId = GENERATED.getMethod(
+                Type.STRING, "call", Type.INT, Type.INT, Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localA = code.getParameter(0, Type.INT);
+        Local<Integer> localB = code.getParameter(1, Type.INT);
+        Local<Integer> localC = code.getParameter(2, Type.INT);
+        Local<String> localResult = code.newLocal(Type.STRING);
         Label catchInner = code.newLabel();
         Label catchOuter = code.newLabel();
 
-        Type<IllegalArgumentException> iaeType = generator.getType(IllegalArgumentException.class);
+        Type<IllegalArgumentException> iaeType = Type.get(IllegalArgumentException.class);
         code.addCatchClause(iaeType, catchOuter);
 
-        Method<?, ?> thrower = dexGeneratorTestType.getMethod(voidType, "thrower", intType);
+        MethodId<?, ?> thrower = TEST_TYPE.getMethod(Type.VOID, "thrower", Type.INT);
         code.invokeStatic(thrower, null, localA);
 
         // for the inner catch clause, we stash the old label and put it back afterwards.
@@ -1352,7 +1337,7 @@
         code.loadConstant(localResult, "OUTER");
         code.returnValue(localResult);
 
-        java.lang.reflect.Method method = getMethod();
+        Method method = getMethod();
         assertEquals("OUTER", method.invoke(null, 1, 0, 0));
         assertEquals("INNER", method.invoke(null, 0, 1, 0));
         assertEquals("OUTER", method.invoke(null, 0, 0, 1));
@@ -1365,10 +1350,10 @@
          *   throw new IllegalStateException();
          * }
          */
-        Code code = generatedType.getMethod(voidType, "call")
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Type<IllegalStateException> iseType = generator.getType(IllegalStateException.class);
-        Method<IllegalStateException, Void> iseConstructor = iseType.getConstructor();
+        MethodId<?, Void> methodId = GENERATED.getMethod(Type.VOID, "call");
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Type<IllegalStateException> iseType = Type.get(IllegalStateException.class);
+        MethodId<IllegalStateException, Void> iseConstructor = iseType.getConstructor();
         Local<IllegalStateException> localIse = code.newLocal(iseType);
         code.newInstance(localIse, iseConstructor);
         code.throwValue(localIse);
@@ -1385,14 +1370,15 @@
         /*
          * public static void call(int unused1, long unused2, long unused3) {}
          */
-        Code code = generatedType.getMethod(voidType, "call", intType, longType, longType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        MethodId<?, Void> methodId = GENERATED.getMethod(
+                Type.VOID, "call", Type.INT, Type.LONG, Type.LONG);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         code.returnVoid();
         getMethod().invoke(null, 1, 2, 3);
     }
 
     public void testFloatingPointCompare() throws Exception {
-        java.lang.reflect.Method floatG = floatingPointCompareMethod(floatType, 1);
+        Method floatG = floatingPointCompareMethod(Type.FLOAT, 1);
         assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY));
         assertEquals(-1, floatG.invoke(null, 1.0f, 2.0f));
         assertEquals(0, floatG.invoke(null, 1.0f, 1.0f));
@@ -1402,7 +1388,7 @@
         assertEquals(1, floatG.invoke(null, Float.NaN, Float.NaN));
         assertEquals(1, floatG.invoke(null, Float.NaN, Float.POSITIVE_INFINITY));
 
-        java.lang.reflect.Method floatL = floatingPointCompareMethod(floatType, -1);
+        Method floatL = floatingPointCompareMethod(Type.FLOAT, -1);
         assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY));
         assertEquals(-1, floatL.invoke(null, 1.0f, 2.0f));
         assertEquals(0, floatL.invoke(null, 1.0f, 1.0f));
@@ -1412,7 +1398,7 @@
         assertEquals(-1, floatL.invoke(null, Float.NaN, Float.NaN));
         assertEquals(-1, floatL.invoke(null, Float.NaN, Float.POSITIVE_INFINITY));
 
-        java.lang.reflect.Method doubleG = floatingPointCompareMethod(doubleType, 1);
+        Method doubleG = floatingPointCompareMethod(Type.DOUBLE, 1);
         assertEquals(-1, doubleG.invoke(null, 1.0, Double.POSITIVE_INFINITY));
         assertEquals(-1, doubleG.invoke(null, 1.0, 2.0));
         assertEquals(0, doubleG.invoke(null, 1.0, 1.0));
@@ -1422,7 +1408,7 @@
         assertEquals(1, doubleG.invoke(null, Double.NaN, Double.NaN));
         assertEquals(1, doubleG.invoke(null, Double.NaN, Double.POSITIVE_INFINITY));
 
-        java.lang.reflect.Method doubleL = floatingPointCompareMethod(doubleType, -1);
+        Method doubleL = floatingPointCompareMethod(Type.DOUBLE, -1);
         assertEquals(-1, doubleL.invoke(null, 1.0, Double.POSITIVE_INFINITY));
         assertEquals(-1, doubleL.invoke(null, 1.0, 2.0));
         assertEquals(0, doubleL.invoke(null, 1.0, 1.0));
@@ -1433,7 +1419,7 @@
         assertEquals(-1, doubleL.invoke(null, Double.NaN, Double.POSITIVE_INFINITY));
     }
 
-    private <T extends Number> java.lang.reflect.Method floatingPointCompareMethod(
+    private <T extends Number> Method floatingPointCompareMethod(
             Type<T> valueType, int nanValue) throws Exception {
         /*
          * public static int call(float a, float b) {
@@ -1442,11 +1428,11 @@
          * }
          */
         reset();
-        Code code = generatedType.getMethod(intType, "call", valueType, valueType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", valueType, valueType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<T> localA = code.getParameter(0, valueType);
         Local<T> localB = code.getParameter(1, valueType);
-        Local<Integer> localResult = code.newLocal(intType);
+        Local<Integer> localResult = code.newLocal(Type.INT);
         code.compare(localA, localB, localResult, nanValue);
         code.returnValue(localResult);
         return getMethod();
@@ -1459,15 +1445,15 @@
          *   return result;
          * }
          */
-        Code code = generatedType.getMethod(intType, "call", longType, longType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Long> localA = code.getParameter(0, longType);
-        Local<Long> localB = code.getParameter(1, longType);
-        Local<Integer> localResult = code.newLocal(intType);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.LONG, Type.LONG);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Long> localA = code.getParameter(0, Type.LONG);
+        Local<Long> localB = code.getParameter(1, Type.LONG);
+        Local<Integer> localResult = code.newLocal(Type.INT);
         code.compare(localA, localB, localResult);
         code.returnValue(localResult);
 
-        java.lang.reflect.Method method = getMethod();
+        Method method = getMethod();
         assertEquals(0, method.invoke(null, Long.MIN_VALUE, Long.MIN_VALUE));
         assertEquals(-1, method.invoke(null, Long.MIN_VALUE, 0));
         assertEquals(-1, method.invoke(null, Long.MIN_VALUE, Long.MAX_VALUE));
@@ -1480,28 +1466,28 @@
     }
 
     public void testArrayLength() throws Exception {
-        java.lang.reflect.Method booleanArrayLength = arrayLengthMethod(booleanArrayType);
+        Method booleanArrayLength = arrayLengthMethod(BOOLEAN_ARRAY);
         assertEquals(0, booleanArrayLength.invoke(null, new Object[] { new boolean[0] }));
         assertEquals(5, booleanArrayLength.invoke(null, new Object[] { new boolean[5] }));
 
-        java.lang.reflect.Method intArrayLength = arrayLengthMethod(intArrayType);
+        Method intArrayLength = arrayLengthMethod(INT_ARRAY);
         assertEquals(0, intArrayLength.invoke(null, new Object[] { new int[0] }));
         assertEquals(5, intArrayLength.invoke(null, new Object[] { new int[5] }));
 
-        java.lang.reflect.Method longArrayLength = arrayLengthMethod(longArrayType);
+        Method longArrayLength = arrayLengthMethod(LONG_ARRAY);
         assertEquals(0, longArrayLength.invoke(null, new Object[] { new long[0] }));
         assertEquals(5, longArrayLength.invoke(null, new Object[] { new long[5] }));
 
-        java.lang.reflect.Method objectArrayLength = arrayLengthMethod(objectArrayType);
+        Method objectArrayLength = arrayLengthMethod(OBJECT_ARRAY);
         assertEquals(0, objectArrayLength.invoke(null, new Object[] { new Object[0] }));
         assertEquals(5, objectArrayLength.invoke(null, new Object[] { new Object[5] }));
 
-        java.lang.reflect.Method long2dArrayLength = arrayLengthMethod(long2dArrayType);
+        Method long2dArrayLength = arrayLengthMethod(LONG_2D_ARRAY);
         assertEquals(0, long2dArrayLength.invoke(null, new Object[] { new long[0][0] }));
         assertEquals(5, long2dArrayLength.invoke(null, new Object[] { new long[5][10] }));
     }
 
-    private <T> java.lang.reflect.Method arrayLengthMethod(Type<T> valueType) throws Exception {
+    private <T> Method arrayLengthMethod(Type<T> valueType) throws Exception {
         /*
          * public static int call(long[] array) {
          *   int result = array.length;
@@ -1509,41 +1495,41 @@
          * }
          */
         reset();
-        Code code = generatedType.getMethod(intType, "call", valueType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", valueType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<T> localArray = code.getParameter(0, valueType);
-        Local<Integer> localResult = code.newLocal(intType);
+        Local<Integer> localResult = code.newLocal(Type.INT);
         code.arrayLength(localArray, localResult);
         code.returnValue(localResult);
         return getMethod();
     }
 
     public void testNewArray() throws Exception {
-        java.lang.reflect.Method newBooleanArray = newArrayMethod(booleanArrayType);
+        Method newBooleanArray = newArrayMethod(BOOLEAN_ARRAY);
         assertEquals("[]", Arrays.toString((boolean[]) newBooleanArray.invoke(null, 0)));
         assertEquals("[false, false, false]",
                 Arrays.toString((boolean[]) newBooleanArray.invoke(null, 3)));
 
-        java.lang.reflect.Method newIntArray = newArrayMethod(intArrayType);
+        Method newIntArray = newArrayMethod(INT_ARRAY);
         assertEquals("[]", Arrays.toString((int[]) newIntArray.invoke(null, 0)));
         assertEquals("[0, 0, 0]", Arrays.toString((int[]) newIntArray.invoke(null, 3)));
 
-        java.lang.reflect.Method newLongArray = newArrayMethod(longArrayType);
+        Method newLongArray = newArrayMethod(LONG_ARRAY);
         assertEquals("[]", Arrays.toString((long[]) newLongArray.invoke(null, 0)));
         assertEquals("[0, 0, 0]", Arrays.toString((long[]) newLongArray.invoke(null, 3)));
 
-        java.lang.reflect.Method newObjectArray = newArrayMethod(objectArrayType);
+        Method newObjectArray = newArrayMethod(OBJECT_ARRAY);
         assertEquals("[]", Arrays.toString((Object[]) newObjectArray.invoke(null, 0)));
         assertEquals("[null, null, null]",
                 Arrays.toString((Object[]) newObjectArray.invoke(null, 3)));
 
-        java.lang.reflect.Method new2dLongArray = newArrayMethod(long2dArrayType);
+        Method new2dLongArray = newArrayMethod(LONG_2D_ARRAY);
         assertEquals("[]", Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 0)));
         assertEquals("[null, null, null]",
                 Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 3)));
     }
 
-    private <T> java.lang.reflect.Method newArrayMethod(Type<T> valueType) throws Exception {
+    private <T> Method newArrayMethod(Type<T> valueType) throws Exception {
         /*
          * public static long[] call(int length) {
          *   long[] result = new long[length];
@@ -1551,9 +1537,9 @@
          * }
          */
         reset();
-        Code code = generatedType.getMethod(valueType, "call", intType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
-        Local<Integer> localLength = code.getParameter(0, intType);
+        MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", Type.INT);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
+        Local<Integer> localLength = code.getParameter(0, Type.INT);
         Local<T> localResult = code.newLocal(valueType);
         code.newArray(localLength, localResult);
         code.returnValue(localResult);
@@ -1561,33 +1547,33 @@
     }
 
     public void testReadAndWriteArray() throws Exception {
-        java.lang.reflect.Method swapBooleanArray = arraySwapMethod(booleanArrayType, booleanType);
+        Method swapBooleanArray = arraySwapMethod(BOOLEAN_ARRAY, Type.BOOLEAN);
         boolean[] booleans = new boolean[3];
         assertEquals(false, swapBooleanArray.invoke(null, booleans, 1, true));
         assertEquals("[false, true, false]", Arrays.toString(booleans));
 
-        java.lang.reflect.Method swapIntArray = arraySwapMethod(intArrayType, intType);
+        Method swapIntArray = arraySwapMethod(INT_ARRAY, Type.INT);
         int[] ints = new int[3];
         assertEquals(0, swapIntArray.invoke(null, ints, 1, 5));
         assertEquals("[0, 5, 0]", Arrays.toString(ints));
 
-        java.lang.reflect.Method swapLongArray = arraySwapMethod(longArrayType, longType);
+        Method swapLongArray = arraySwapMethod(LONG_ARRAY, Type.LONG);
         long[] longs = new long[3];
         assertEquals(0L, swapLongArray.invoke(null, longs, 1, 6L));
         assertEquals("[0, 6, 0]", Arrays.toString(longs));
 
-        java.lang.reflect.Method swapObjectArray = arraySwapMethod(objectArrayType, objectType);
+        Method swapObjectArray = arraySwapMethod(OBJECT_ARRAY, Type.OBJECT);
         Object[] objects = new Object[3];
         assertEquals(null, swapObjectArray.invoke(null, objects, 1, "X"));
         assertEquals("[null, X, null]", Arrays.toString(objects));
 
-        java.lang.reflect.Method swapLong2dArray = arraySwapMethod(long2dArrayType, longArrayType);
+        Method swapLong2dArray = arraySwapMethod(LONG_2D_ARRAY, LONG_ARRAY);
         long[][] longs2d = new long[3][];
         assertEquals(null, swapLong2dArray.invoke(null, longs2d, 1, new long[] { 7 }));
         assertEquals("[null, [7], null]", Arrays.deepToString(longs2d));
     }
 
-    private <A, T> java.lang.reflect.Method arraySwapMethod(Type<A> arrayType, Type<T> singleType)
+    private <A, T> Method arraySwapMethod(Type<A> arrayType, Type<T> singleType)
             throws Exception {
         /*
          * public static long swap(long[] array, int index, long newValue) {
@@ -1597,10 +1583,11 @@
          * }
          */
         reset();
-        Code code = generatedType.getMethod(singleType, "call", arrayType, intType, singleType)
-                .declare(ACC_PUBLIC | ACC_STATIC);
+        MethodId<?, T> methodId = GENERATED.getMethod(
+                singleType, "call", arrayType, Type.INT, singleType);
+        Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC);
         Local<A> localArray = code.getParameter(0, arrayType);
-        Local<Integer> localIndex = code.getParameter(1, intType);
+        Local<Integer> localIndex = code.getParameter(1, Type.INT);
         Local<T> localNewValue = code.getParameter(2, singleType);
         Local<T> localResult = code.newLocal(singleType);
         code.aget(localArray, localIndex, localResult);
@@ -1616,18 +1603,18 @@
     // TODO: don't generate multiple times (?)
 
     private void addDefaultConstructor() {
-        Code code = generatedType.getConstructor().declare(ACC_PUBLIC | ACC_CONSTRUCTOR);
-        Local<?> thisRef = code.getThis(generatedType);
-        code.invokeDirect(objectType.getConstructor(), null, thisRef);
+        Code code = generator.declare(GENERATED.getConstructor(), ACC_PUBLIC | ACC_CONSTRUCTOR);
+        Local<?> thisRef = code.getThis(GENERATED);
+        code.invokeDirect(Type.OBJECT.getConstructor(), null, thisRef);
         code.returnVoid();
     }
 
     /**
      * Returns the generated method.
      */
-    private java.lang.reflect.Method getMethod() throws Exception {
+    private Method getMethod() throws Exception {
         Class<?> generated = loadAndGenerate();
-        for (java.lang.reflect.Method method : generated.getMethods()) {
+        for (Method method : generated.getMethods()) {
             if (method.getName().equals("call")) {
                 return method;
             }
diff --git a/dx/junit-tests/com/android/dx/gen/TypeTest.java b/dx/junit-tests/com/android/dx/gen/TypeTest.java
index d713879..c2f4f04 100644
--- a/dx/junit-tests/com/android/dx/gen/TypeTest.java
+++ b/dx/junit-tests/com/android/dx/gen/TypeTest.java
@@ -23,11 +23,11 @@
     private final DexGenerator generator = new DexGenerator();
 
     public void testGetType() {
-        assertEquals("Ljava/lang/String;", generator.getType(String.class).getName());
-        assertEquals("[Ljava/lang/String;", generator.getType(String[].class).getName());
-        assertEquals("[[Ljava/lang/String;", generator.getType(String[][].class).getName());
-        assertEquals("I", generator.getType(int.class).getName());
-        assertEquals("[I", generator.getType(int[].class).getName());
-        assertEquals("[[I", generator.getType(int[][].class).getName());
+        assertEquals("Ljava/lang/String;", Type.get(String.class).getName());
+        assertEquals("[Ljava/lang/String;", Type.get(String[].class).getName());
+        assertEquals("[[Ljava/lang/String;", Type.get(String[][].class).getName());
+        assertEquals("I", Type.get(int.class).getName());
+        assertEquals("[I", Type.get(int[].class).getName());
+        assertEquals("[[I", Type.get(int[][].class).getName());
     }
 }
diff --git a/dx/src/com/android/dx/dex/code/OutputFinisher.java b/dx/src/com/android/dx/dex/code/OutputFinisher.java
index 51ef9a2..f28e0d3 100644
--- a/dx/src/com/android/dx/dex/code/OutputFinisher.java
+++ b/dx/src/com/android/dx/dex/code/OutputFinisher.java
@@ -28,6 +28,7 @@
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.cst.CstUtf8;
 import com.android.dx.rop.type.Type;
+
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.HashSet;
diff --git a/dx/src/com/android/dx/gen/Canonicalizer.java b/dx/src/com/android/dx/gen/Canonicalizer.java
deleted file mode 100644
index 8a76f1b..0000000
--- a/dx/src/com/android/dx/gen/Canonicalizer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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.android.dx.gen;
-
-import java.util.AbstractSet;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-final class Canonicalizer<T> extends AbstractSet<T> implements Set<T> {
-    private final Map<T, T> map = new HashMap<T, T>();
-
-    public <S extends T> S get(S s) {
-        @SuppressWarnings("unchecked") // equals() guarantees that the types match
-        S result = (S) map.get(s);
-        if (result != null) {
-            return result;
-        }
-        map.put(s, s);
-        return s;
-    }
-
-    @Override public int size() {
-        return map.size();
-    }
-
-    public Iterator<T> iterator() {
-        return map.keySet().iterator();
-    }
-}
diff --git a/dx/src/com/android/dx/gen/Code.java b/dx/src/com/android/dx/gen/Code.java
index 756afed..b44d01c 100644
--- a/dx/src/com/android/dx/gen/Code.java
+++ b/dx/src/com/android/dx/gen/Code.java
@@ -44,7 +44,7 @@
  * Builds a sequence of instructions.
  */
 public final class Code {
-    private final Method<?, ?> method;
+    private final MethodId<?, ?> method;
     /**
      * All allocated labels. Although the order of the labels in this list
      * shouldn't impact behavior, it is used to determine basic block indices.
@@ -68,20 +68,25 @@
     private final List<Label> catchLabels = new ArrayList<Label>();
     private StdTypeList catches = StdTypeList.EMPTY;
 
-    public Code(Method<?, ?> method) {
-        this.method = method;
-        this.thisLocal = method.isStatic()
+    Code(DexGenerator.MethodDeclaration methodDeclaration) {
+        this.method = methodDeclaration.method;
+        this.thisLocal = methodDeclaration.isStatic()
                 ? null
-                : new Local<Object>(this, method.declaringType, Local.InitialValue.THIS);
+                : Local.get(this, method.declaringType);
         for (Type<?> parameter : method.parameters.types) {
-            parameters.add(new Local<Object>(this, parameter, Local.InitialValue.PARAMETER));
+            parameters.add(Local.get(this, parameter));
         }
         this.currentLabel = newLabel();
         this.currentLabel.marked = true;
     }
 
     public <T> Local<T> newLocal(Type<T> type) {
-        return allocateLocal(type, Local.InitialValue.NONE);
+        if (localsInitialized) {
+            throw new IllegalStateException("Cannot allocate locals after adding instructions");
+        }
+        Local<T> result = Local.get(this, type);
+        locals.add(result);
+        return result;
     }
 
     public <T> Local<T> getParameter(int index, Type<T> type) {
@@ -104,15 +109,6 @@
         return (Local<T>) local;
     }
 
-    private <T> Local<T> allocateLocal(Type<T> type, Local.InitialValue initialValue) {
-        if (localsInitialized) {
-            throw new IllegalStateException("Cannot allocate locals after adding instructions");
-        }
-        Local<T> result = new Local<T>(this, type, initialValue);
-        locals.add(result);
-        return result;
-    }
-
     /**
      * Assigns registers to locals. From the spec:
      *  "the N arguments to a method land in the last N registers of the
@@ -392,31 +388,31 @@
 
     // instructions: fields
 
-    public <T, R> void iget(Field<T, R> field, Local<T> instance, Local<R> target) {
+    public <D, V> void iget(FieldId<D, V> fieldId, Local<D> instance, Local<V> target) {
         addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
-                RegisterSpecList.make(instance.spec()), catches, field.constant));
+                RegisterSpecList.make(instance.spec()), catches, fieldId.constant));
         moveResult(target, true);
     }
 
-    public <T, R> void iput(Field<T, R> field, Local<T> instance, Local<R> source) {
+    public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) {
         addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
-                RegisterSpecList.make(source.spec(), instance.spec()), catches, field.constant));
+                RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant));
     }
 
-    public <T> void sget(Field<?, T> field, Local<T> target) {
+    public <V> void sget(FieldId<?, V> fieldId, Local<V> target) {
         addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
-                RegisterSpecList.EMPTY, catches, field.constant));
+                RegisterSpecList.EMPTY, catches, fieldId.constant));
         moveResult(target, true);
     }
 
-    public <T> void sput(Field<?, T> field, Local<T> source) {
+    public <V> void sput(FieldId<?, V> fieldId, Local<V> source) {
         addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
-                RegisterSpecList.make(source.spec()), catches, field.constant));
+                RegisterSpecList.make(source.spec()), catches, fieldId.constant));
     }
 
     // instructions: invoke
 
-    public <T> void newInstance(Local<T> target, Method<T, Void> constructor, Local<?>... args) {
+    public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) {
         if (target == null) {
             throw new IllegalArgumentException();
         }
@@ -426,32 +422,32 @@
         invokeDirect(constructor, null, target, args);
     }
 
-    public <R> void invokeStatic(Method<?, R> method, Local<? super R> target, Local<?>... args) {
+    public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) {
         invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args);
     }
 
-    public <I, R> void invokeVirtual(Method<I, R> method, Local<? super R> target,
-            Local<? extends I> object, Local<?>... args) {
+    public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target,
+            Local<? extends D> object, Local<?>... args) {
         invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, object, args);
     }
 
-    public <I, R> void invokeDirect(Method<I, R> method, Local<? super R> target,
-            Local<? extends I> object, Local<?>... args) {
+    public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target,
+            Local<? extends D> object, Local<?>... args) {
         invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, object, args);
     }
 
-    public <I, R> void invokeSuper(Method<I, R> method, Local<? super R> target,
-            Local<? extends I> object, Local<?>... args) {
+    public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target,
+            Local<? extends D> object, Local<?>... args) {
         invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, object, args);
     }
 
-    public <I, R> void invokeInterface(Method<I, R> method, Local<? super R> target,
-            Local<? extends I> object, Local<?>... args) {
+    public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target,
+            Local<? extends D> object, Local<?>... args) {
         invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, object, args);
     }
 
-    private <I, R> void invoke(Rop rop, Method<I, R> method, Local<? super R> target,
-            Local<? extends I> object, Local<?>... args) {
+    private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target,
+            Local<? extends D> object, Local<?>... args) {
         addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args),
                 catches, method.constant));
         if (target != null) {
@@ -501,7 +497,7 @@
     // instructions: return
 
     public void returnVoid() {
-        if (!method.returnType.isVoid()) {
+        if (!method.returnType.equals(Type.VOID)) {
             throw new IllegalArgumentException("declared " + method.returnType
                     + " but returned void");
         }
diff --git a/dx/src/com/android/dx/gen/Constants.java b/dx/src/com/android/dx/gen/Constants.java
index 71d7ac4..255c2e4 100644
--- a/dx/src/com/android/dx/gen/Constants.java
+++ b/dx/src/com/android/dx/gen/Constants.java
@@ -62,8 +62,7 @@
         } else if (value instanceof String) {
             return new CstString((String) value);
         } else if (value instanceof Class) {
-            String name = Type.getTypeName((Class<?>) value);
-            return new CstType(com.android.dx.rop.type.Type.internReturnType(name));
+            return new CstType(Type.get((Class<?>) value).ropType);
         } else if (value instanceof Type) {
             return new CstType(((Type) value).ropType);
         } else {
diff --git a/dx/src/com/android/dx/gen/DexGenerator.java b/dx/src/com/android/dx/gen/DexGenerator.java
index 5355cec..4641335 100644
--- a/dx/src/com/android/dx/gen/DexGenerator.java
+++ b/dx/src/com/android/dx/gen/DexGenerator.java
@@ -17,11 +17,29 @@
 package com.android.dx.gen;
 
 import com.android.dx.dex.DexFormat;
+import com.android.dx.dex.DexOptions;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.code.RopTranslator;
+import com.android.dx.dex.file.ClassDefItem;
 import com.android.dx.dex.file.DexFile;
+import com.android.dx.dex.file.EncodedField;
+import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.rop.code.AccessFlags;
+import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR;
+import static com.android.dx.rop.code.AccessFlags.ACC_PRIVATE;
+import static com.android.dx.rop.code.AccessFlags.ACC_STATIC;
+import com.android.dx.rop.code.LocalVariableInfo;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.StdTypeList;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 
@@ -29,17 +47,57 @@
  * Define types, fields and methods.
  */
 public final class DexGenerator {
-    private final Canonicalizer<Type<?>> types = new Canonicalizer<Type<?>>();
+    private final Map<Type<?>, TypeDeclaration> types
+            = new LinkedHashMap<Type<?>, TypeDeclaration>();
 
-    /**
-     * @param name a descriptor like "(Ljava/lang/Class;[I)Ljava/lang/Object;".
-     */
-    public <T> Type<T> getType(String name) {
-        return types.get(new Type<T>(this, name));
+    private TypeDeclaration getTypeDeclaration(Type<?> type) {
+        TypeDeclaration result = types.get(type);
+        if (result == null) {
+            result = new TypeDeclaration(type);
+            types.put(type, result);
+        }
+        return result;
     }
 
-    public <T> Type<T> getType(Class<T> type) {
-        return types.get(new Type<T>(this, type));
+    /**
+     * @param flags any flags masked by {@link com.android.dx.rop.code.AccessFlags#CLASS_FLAGS}.
+     */
+    public void declare(Type<?> type, String sourceFile, int flags,
+            Type<?> supertype, Type<?>... interfaces) {
+        TypeDeclaration declaration = getTypeDeclaration(type);
+        if (declaration.declared) {
+            throw new IllegalStateException("already declared: " + type);
+        }
+        declaration.declared = true;
+        declaration.flags = flags;
+        declaration.supertype = supertype;
+        declaration.sourceFile = sourceFile;
+        declaration.interfaces = new TypeList(interfaces);
+    }
+
+    /**
+     * @param flags any flags masked by {@link com.android.dx.rop.code.AccessFlags#METHOD_FLAGS}.
+     */
+    public Code declare(MethodId<?, ?> method, int flags) {
+        TypeDeclaration typeDeclaration = getTypeDeclaration(method.declaringType);
+        if (typeDeclaration.methods.containsKey(method)) {
+            throw new IllegalStateException("already declared: " + method);
+        }
+        MethodDeclaration methodDeclaration = new MethodDeclaration(method, flags);
+        typeDeclaration.methods.put(method, methodDeclaration);
+        return methodDeclaration.code;
+    }
+
+    /**
+     * @param flags any flags masked by {@link AccessFlags#FIELD_FLAGS}.
+     */
+    public void declare(FieldId<?, ?> fieldId, int flags, Object staticValue) {
+        TypeDeclaration typeDeclaration = getTypeDeclaration(fieldId.declaringType);
+        if (typeDeclaration.fields.containsKey(fieldId)) {
+            throw new IllegalStateException("already declared: " + fieldId);
+        }
+        FieldDeclaration fieldDeclaration = new FieldDeclaration(fieldId, flags, staticValue);
+        typeDeclaration.fields.put(fieldId, fieldDeclaration);
     }
 
     /**
@@ -48,23 +106,8 @@
     public byte[] generate() {
         DexFile outputDex = new DexFile();
 
-        for (Type<?> type : types) {
-            if (type.isDeclared()) {
-                outputDex.add(type.toClassDefItem());
-            } else {
-                for (Method<?, ?> m : type.getMethods()) {
-                    if (m.isDeclared()) {
-                        throw new IllegalStateException(
-                                "Undeclared type " + type + " declares " + m);
-                    }
-                }
-                for (Field<?, ?> f : type.getFields()) {
-                    if (f.isDeclared()) {
-                        throw new IllegalStateException(
-                                "Undeclared type " + type + " declares " + f);
-                    }
-                }
-            }
+        for (TypeDeclaration typeDeclaration : types.values()) {
+            outputDex.add(typeDeclaration.toClassDefItem());
         }
 
         try {
@@ -110,4 +153,109 @@
             throw new AssertionError();
         }
     }
+
+    private static class TypeDeclaration {
+        private final Type<?> type;
+
+        /** declared state */
+        private boolean declared;
+        private int flags;
+        private Type<?> supertype;
+        private String sourceFile;
+        private TypeList interfaces;
+
+        private final Map<FieldId, FieldDeclaration> fields
+                = new LinkedHashMap<FieldId, FieldDeclaration>();
+        private final Map<MethodId, MethodDeclaration> methods
+                = new LinkedHashMap<MethodId, MethodDeclaration>();
+
+        TypeDeclaration(Type<?> type) {
+            this.type = type;
+        }
+
+        ClassDefItem toClassDefItem() {
+            if (!declared) {
+                throw new IllegalStateException("Undeclared type " + type + " declares members: "
+                        + fields.keySet() + " " + methods.keySet());
+            }
+
+            DexOptions dexOptions = new DexOptions();
+            dexOptions.enableExtendedOpcodes = false;
+
+            CstType thisType = type.constant;
+
+            ClassDefItem out = new ClassDefItem(thisType, flags, supertype.constant,
+                    interfaces.ropTypes, new CstUtf8(sourceFile));
+
+            for (MethodDeclaration method : methods.values()) {
+                EncodedMethod encoded = method.toEncodedMethod(dexOptions);
+                if (method.isDirect()) {
+                    out.addDirectMethod(encoded);
+                } else {
+                    out.addVirtualMethod(encoded);
+                }
+            }
+            for (FieldDeclaration field : fields.values()) {
+                EncodedField encoded = field.toEncodedField();
+                if (field.isStatic()) {
+                    out.addStaticField(encoded, Constants.getConstant(field.staticValue));
+                } else {
+                    out.addInstanceField(encoded);
+                }
+            }
+
+            return out;
+        }
+    }
+
+    static class FieldDeclaration {
+        final FieldId<?, ?> fieldId;
+        private final int accessFlags;
+        private final Object staticValue;
+
+        FieldDeclaration(FieldId<?, ?> fieldId, int accessFlags, Object staticValue) {
+            if ((accessFlags & (AccessFlags.ACC_STATIC)) == 0 && staticValue != null) {
+                throw new IllegalArgumentException("instance fields may not have a value");
+            }
+            this.fieldId = fieldId;
+            this.accessFlags = accessFlags;
+            this.staticValue = staticValue;
+        }
+
+        EncodedField toEncodedField() {
+            return new EncodedField(fieldId.constant, accessFlags);
+        }
+
+        public boolean isStatic() {
+            return (accessFlags & (AccessFlags.ACC_STATIC)) != 0;
+        }
+    }
+
+    static class MethodDeclaration {
+        final MethodId<?, ?> method;
+        private final int flags;
+        private final Code code;
+
+        public MethodDeclaration(MethodId<?, ?> method, int flags) {
+            this.method = method;
+            this.flags = flags;
+            this.code = new Code(this);
+        }
+
+        boolean isStatic() {
+            return (flags & ACC_STATIC) != 0;
+        }
+
+        boolean isDirect() {
+            return (flags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) != 0;
+        }
+
+        EncodedMethod toEncodedMethod(DexOptions dexOptions) {
+            RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0);
+            LocalVariableInfo locals = null;
+            DalvCode dalvCode = RopTranslator.translate(
+                    ropMethod, PositionList.NONE, locals, code.paramSize(), dexOptions);
+            return new EncodedMethod(method.constant, flags, dalvCode, StdTypeList.EMPTY);
+        }
+    }
 }
diff --git a/dx/src/com/android/dx/gen/Field.java b/dx/src/com/android/dx/gen/Field.java
deleted file mode 100644
index f6a42cf..0000000
--- a/dx/src/com/android/dx/gen/Field.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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.android.dx.gen;
-
-import com.android.dx.dex.file.EncodedField;
-import com.android.dx.rop.code.AccessFlags;
-import com.android.dx.rop.cst.CstFieldRef;
-import com.android.dx.rop.cst.CstNat;
-import com.android.dx.rop.cst.CstUtf8;
-
-/**
- * A field.
- */
-public final class Field<T, R> {
-    final Type<T> declaringType;
-    final Type<R> type;
-    final String name;
-
-    /** cached converted state */
-    final CstNat nat;
-    final CstFieldRef constant;
-
-    /** declared state */
-    private boolean declared;
-    private int accessFlags;
-    private Object staticValue;
-
-    Field(Type<T> declaringType, Type<R> type, String name) {
-        if (declaringType == null || type == null || name == null) {
-            throw new NullPointerException();
-        }
-        this.declaringType = declaringType;
-        this.type = type;
-        this.name = name;
-        this.nat = new CstNat(new CstUtf8(name), new CstUtf8(type.name));
-        this.constant = new CstFieldRef(declaringType.constant, nat);
-    }
-
-    public Type<T> getDeclaringType() {
-        return declaringType;
-    }
-
-    public Type<R> getType() {
-        return type;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    /**
-     * @param accessFlags any flags masked by {@link AccessFlags#FIELD_FLAGS}.
-     */
-    public void declare(int accessFlags, Object staticValue) {
-        if (declared) {
-            throw new IllegalStateException("already declared: " + this);
-        }
-        if ((accessFlags & (AccessFlags.ACC_STATIC)) == 0 && staticValue != null) {
-            throw new IllegalArgumentException("instance fields may not have a value");
-        }
-        this.declared = true;
-        this.accessFlags = accessFlags;
-        this.staticValue = staticValue;
-    }
-
-    public Object getStaticValue() {
-        return staticValue;
-    }
-
-    boolean isDeclared() {
-        return declared;
-    }
-
-    public boolean isStatic() {
-        if (!declared) {
-            throw new IllegalStateException();
-        }
-        return (accessFlags & (AccessFlags.ACC_STATIC)) != 0;
-    }
-
-    EncodedField toEncodedField() {
-        if (!declared) {
-            throw new IllegalStateException();
-        }
-        return new EncodedField(constant, accessFlags);
-    }
-
-    @Override public boolean equals(Object o) {
-        return o instanceof Field
-                && ((Field) o).declaringType.equals(declaringType)
-                && ((Field) o).name.equals(name);
-    }
-
-    @Override public int hashCode() {
-        return declaringType.hashCode() + 37 * name.hashCode();
-    }
-
-    @Override public String toString() {
-        return declaringType + "." + name;
-    }
-}
diff --git a/dx/src/com/android/dx/gen/FieldId.java b/dx/src/com/android/dx/gen/FieldId.java
new file mode 100644
index 0000000..76dc151
--- /dev/null
+++ b/dx/src/com/android/dx/gen/FieldId.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.android.dx.gen;
+
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * A field.
+ */
+public final class FieldId<D, V> {
+    final Type<D> declaringType;
+    final Type<V> type;
+    final String name;
+
+    /** cached converted state */
+    final CstNat nat;
+    final CstFieldRef constant;
+
+    FieldId(Type<D> declaringType, Type<V> type, String name) {
+        if (declaringType == null || type == null || name == null) {
+            throw new NullPointerException();
+        }
+        this.declaringType = declaringType;
+        this.type = type;
+        this.name = name;
+        this.nat = new CstNat(new CstUtf8(name), new CstUtf8(type.name));
+        this.constant = new CstFieldRef(declaringType.constant, nat);
+    }
+
+    public Type<D> getDeclaringType() {
+        return declaringType;
+    }
+
+    public Type<V> getType() {
+        return type;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override public boolean equals(Object o) {
+        return o instanceof FieldId
+                && ((FieldId<?, ?>) o).declaringType.equals(declaringType)
+                && ((FieldId<?, ?>) o).name.equals(name);
+    }
+
+    @Override public int hashCode() {
+        return declaringType.hashCode() + 37 * name.hashCode();
+    }
+
+    @Override public String toString() {
+        return declaringType + "." + name;
+    }
+}
diff --git a/dx/src/com/android/dx/gen/Local.java b/dx/src/com/android/dx/gen/Local.java
index 4f74b93..b98759c 100644
--- a/dx/src/com/android/dx/gen/Local.java
+++ b/dx/src/com/android/dx/gen/Local.java
@@ -23,15 +23,17 @@
  */
 public final class Local<T> {
     private final Code code;
-    final Type type;
-    final InitialValue initialValue;
+    final Type<T> type;
     private int reg = -1;
     private RegisterSpec spec;
 
-    Local(Code code, Type type, InitialValue initialValue) {
+    private Local(Code code, Type<T> type) {
         this.code = code;
         this.type = type;
-        this.initialValue = initialValue;
+    }
+
+    static <T> Local<T> get(Code code, Type<T> type) {
+        return new Local<T>(code, type);
     }
 
     /**
@@ -41,7 +43,7 @@
      */
     int initialize(int reg) {
         this.reg = reg;
-        this.spec = RegisterSpec.make(reg, type.getRopType());
+        this.spec = RegisterSpec.make(reg, type.ropType);
         return size();
     }
 
@@ -66,8 +68,4 @@
     @Override public String toString() {
         return "v" + reg + "(" + type + ")";
     }
-
-    enum InitialValue {
-        NONE, THIS, PARAMETER
-    }
 }
diff --git a/dx/src/com/android/dx/gen/Method.java b/dx/src/com/android/dx/gen/Method.java
deleted file mode 100644
index f8b9183..0000000
--- a/dx/src/com/android/dx/gen/Method.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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.android.dx.gen;
-
-import com.android.dx.dex.DexOptions;
-import com.android.dx.dex.code.DalvCode;
-import com.android.dx.dex.code.PositionList;
-import com.android.dx.dex.code.RopTranslator;
-import com.android.dx.dex.file.EncodedMethod;
-import com.android.dx.rop.code.AccessFlags;
-import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR;
-import static com.android.dx.rop.code.AccessFlags.ACC_PRIVATE;
-import static com.android.dx.rop.code.AccessFlags.ACC_STATIC;
-import com.android.dx.rop.code.LocalVariableInfo;
-import com.android.dx.rop.code.RopMethod;
-import com.android.dx.rop.cst.CstMethodRef;
-import com.android.dx.rop.cst.CstNat;
-import com.android.dx.rop.cst.CstUtf8;
-import com.android.dx.rop.type.Prototype;
-import com.android.dx.rop.type.StdTypeList;
-import java.util.List;
-
-/**
- * A method or constructor.
- */
-public final class Method<T, R> {
-    final Type<T> declaringType;
-    final Type<R> returnType;
-    final String name;
-    final TypeList parameters;
-
-    /** cached converted state */
-    final CstNat nat;
-    final CstMethodRef constant;
-
-    /** declared state */
-    private boolean declared;
-    private int accessFlags;
-    private Code code;
-
-    Method(Type<T> declaringType, Type<R> returnType, String name, TypeList parameters) {
-        if (declaringType == null || returnType == null || name == null || parameters == null) {
-            throw new NullPointerException();
-        }
-        this.declaringType = declaringType;
-        this.returnType = returnType;
-        this.name = name;
-        this.parameters = parameters;
-        this.nat = new CstNat(new CstUtf8(name), new CstUtf8(descriptor(false)));
-        this.constant = new CstMethodRef(declaringType.constant, nat);
-    }
-
-    public Type<T> getDeclaringType() {
-        return declaringType;
-    }
-
-    public Type<R> getReturnType() {
-        return returnType;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public List<Type<?>> getParameters() {
-        return parameters.asList();
-    }
-
-    /**
-     * Returns a descriptor like "(Ljava/lang/Class;[I)Ljava/lang/Object;".
-     */
-    String descriptor(boolean includeThis) {
-        StringBuilder result = new StringBuilder();
-        result.append("(");
-        if (includeThis) {
-            result.append(declaringType.name);
-        }
-        for (Type t : parameters.types) {
-            result.append(t.name);
-        }
-        result.append(")");
-        result.append(returnType.name);
-        return result.toString();
-    }
-
-    /**
-     * @param accessFlags any flags masked by {@link AccessFlags#METHOD_FLAGS}.
-     */
-    public Code declare(int accessFlags) {
-        if (declared) {
-            throw new IllegalStateException("already declared: " + this);
-        }
-        this.declared = true;
-        this.accessFlags = accessFlags;
-        this.code = new Code(this);
-        return code;
-    }
-
-    boolean isDeclared() {
-        return declared;
-    }
-
-    boolean isStatic() {
-        if (!declared) {
-            throw new IllegalStateException();
-        }
-        return (accessFlags & ACC_STATIC) != 0;
-    }
-
-    boolean isDirect() {
-        if (!declared) {
-            throw new IllegalStateException();
-        }
-        return (accessFlags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) != 0;
-    }
-
-    Prototype prototype(boolean includeThis) {
-        return Prototype.intern(descriptor(includeThis));
-    }
-
-    EncodedMethod toEncodedMethod(DexOptions dexOptions) {
-        if (!declared) {
-            throw new IllegalStateException();
-        }
-        RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0);
-        LocalVariableInfo locals = null;
-        DalvCode dalvCode = RopTranslator.translate(ropMethod, PositionList.NONE, locals,
-                code.paramSize(), dexOptions);
-        return new EncodedMethod(constant, accessFlags, dalvCode, StdTypeList.EMPTY);
-    }
-
-    @Override public boolean equals(Object o) {
-        return o instanceof Method
-                && ((Method) o).declaringType.equals(declaringType)
-                && ((Method) o).name.equals(name)
-                && ((Method) o).parameters.equals(parameters)
-                && ((Method) o).returnType.equals(returnType);
-    }
-
-    @Override public int hashCode() {
-        int result = 17;
-        result = 31 * result + declaringType.hashCode();
-        result = 31 * result + name.hashCode();
-        result = 31 * result + parameters.hashCode();
-        result = 31 * result + returnType.hashCode();
-        return result;
-    }
-
-    @Override public String toString() {
-        return declaringType + "." + name + "(" + parameters + ")";
-    }
-}
diff --git a/dx/src/com/android/dx/gen/MethodId.java b/dx/src/com/android/dx/gen/MethodId.java
new file mode 100644
index 0000000..ecfae78
--- /dev/null
+++ b/dx/src/com/android/dx/gen/MethodId.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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.android.dx.gen;
+
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import java.util.List;
+
+/**
+ * A method or constructor.
+ */
+public final class MethodId<D, R> {
+    final Type<D> declaringType;
+    final Type<R> returnType;
+    final String name;
+    final TypeList parameters;
+
+    /** cached converted state */
+    final CstNat nat;
+    final CstMethodRef constant;
+
+    MethodId(Type<D> declaringType, Type<R> returnType, String name, TypeList parameters) {
+        if (declaringType == null || returnType == null || name == null || parameters == null) {
+            throw new NullPointerException();
+        }
+        this.declaringType = declaringType;
+        this.returnType = returnType;
+        this.name = name;
+        this.parameters = parameters;
+        this.nat = new CstNat(new CstUtf8(name), new CstUtf8(descriptor(false)));
+        this.constant = new CstMethodRef(declaringType.constant, nat);
+    }
+
+    public Type<D> getDeclaringType() {
+        return declaringType;
+    }
+
+    public Type<R> getReturnType() {
+        return returnType;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List<Type<?>> getParameters() {
+        return parameters.asList();
+    }
+
+    /**
+     * Returns a descriptor like "(Ljava/lang/Class;[I)Ljava/lang/Object;".
+     */
+    String descriptor(boolean includeThis) {
+        StringBuilder result = new StringBuilder();
+        result.append("(");
+        if (includeThis) {
+            result.append(declaringType.name);
+        }
+        for (Type t : parameters.types) {
+            result.append(t.name);
+        }
+        result.append(")");
+        result.append(returnType.name);
+        return result.toString();
+    }
+
+    Prototype prototype(boolean includeThis) {
+        return Prototype.intern(descriptor(includeThis));
+    }
+
+    @Override public boolean equals(Object o) {
+        return o instanceof MethodId
+                && ((MethodId<?, ?>) o).declaringType.equals(declaringType)
+                && ((MethodId<?, ?>) o).name.equals(name)
+                && ((MethodId<?, ?>) o).parameters.equals(parameters)
+                && ((MethodId<?, ?>) o).returnType.equals(returnType);
+    }
+
+    @Override public int hashCode() {
+        int result = 17;
+        result = 31 * result + declaringType.hashCode();
+        result = 31 * result + name.hashCode();
+        result = 31 * result + parameters.hashCode();
+        result = 31 * result + returnType.hashCode();
+        return result;
+    }
+
+    @Override public String toString() {
+        return declaringType + "." + name + "(" + parameters + ")";
+    }
+}
diff --git a/dx/src/com/android/dx/gen/Type.java b/dx/src/com/android/dx/gen/Type.java
index 72e804f..3b81f6d 100644
--- a/dx/src/com/android/dx/gen/Type.java
+++ b/dx/src/com/android/dx/gen/Type.java
@@ -16,149 +16,115 @@
 
 package com.android.dx.gen;
 
-import com.android.dx.dex.DexOptions;
-import com.android.dx.dex.file.ClassDefItem;
-import com.android.dx.dex.file.EncodedField;
-import com.android.dx.dex.file.EncodedMethod;
 import com.android.dx.rop.cst.CstType;
-import com.android.dx.rop.cst.CstUtf8;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
 
 /**
- * An interface or class.
+ * A primitive type, interface or class.
  */
 public final class Type<T> {
-    private static final Map<Class<?>, String> PRIMITIVE_TO_TYPE_NAME
-            = new HashMap<Class<?>, String>();
+    /** The {@code boolean} primitive type. */
+    public static final Type<Boolean> BOOLEAN
+            = new Type<Boolean>(com.android.dx.rop.type.Type.BOOLEAN);
+
+    /** The {@code byte} primitive type. */
+    public static final Type<Byte> BYTE = new Type<Byte>(com.android.dx.rop.type.Type.BYTE);
+
+    /** The {@code char} primitive type. */
+    public static final Type<Character> CHAR
+            = new Type<Character>(com.android.dx.rop.type.Type.CHAR);
+
+    /** The {@code double} primitive type. */
+    public static final Type<Double> DOUBLE = new Type<Double>(com.android.dx.rop.type.Type.DOUBLE);
+
+    /** The {@code float} primitive type. */
+    public static final Type<Float> FLOAT = new Type<Float>(com.android.dx.rop.type.Type.FLOAT);
+
+    /** The {@code int} primitive type. */
+    public static final Type<Integer> INT = new Type<Integer>(com.android.dx.rop.type.Type.INT);
+
+    /** The {@code long} primitive type. */
+    public static final Type<Long> LONG = new Type<Long>(com.android.dx.rop.type.Type.LONG);
+
+    /** The {@code short} primitive type. */
+    public static final Type<Short> SHORT = new Type<Short>(com.android.dx.rop.type.Type.SHORT);
+
+    /** The {@code void} primitive type. Only used as a return type. */
+    public static final Type<Void> VOID = new Type<Void>(com.android.dx.rop.type.Type.VOID);
+
+    /** The {@code Object} type. */
+    public static final Type<Object> OBJECT = new Type<Object>(com.android.dx.rop.type.Type.OBJECT);
+
+    /** The {@code String} type. */
+    public static final Type<String> STRING = new Type<String>(com.android.dx.rop.type.Type.STRING);
+
+    private static final Map<Class<?>, Type<?>> PRIMITIVE_TO_TYPE
+            = new HashMap<Class<?>, Type<?>>();
     static {
-        PRIMITIVE_TO_TYPE_NAME.put(boolean.class, "Z");
-        PRIMITIVE_TO_TYPE_NAME.put(byte.class, "B");
-        PRIMITIVE_TO_TYPE_NAME.put(char.class, "C");
-        PRIMITIVE_TO_TYPE_NAME.put(double.class, "D");
-        PRIMITIVE_TO_TYPE_NAME.put(float.class, "F");
-        PRIMITIVE_TO_TYPE_NAME.put(int.class, "I");
-        PRIMITIVE_TO_TYPE_NAME.put(long.class, "J");
-        PRIMITIVE_TO_TYPE_NAME.put(short.class, "S");
-        PRIMITIVE_TO_TYPE_NAME.put(void.class, "V");
+        PRIMITIVE_TO_TYPE.put(boolean.class, BOOLEAN);
+        PRIMITIVE_TO_TYPE.put(byte.class, BYTE);
+        PRIMITIVE_TO_TYPE.put(char.class, CHAR);
+        PRIMITIVE_TO_TYPE.put(double.class, DOUBLE);
+        PRIMITIVE_TO_TYPE.put(float.class, FLOAT);
+        PRIMITIVE_TO_TYPE.put(int.class, INT);
+        PRIMITIVE_TO_TYPE.put(long.class, LONG);
+        PRIMITIVE_TO_TYPE.put(short.class, SHORT);
+        PRIMITIVE_TO_TYPE.put(void.class, VOID);
     }
 
-    final DexGenerator generator;
     final String name;
 
     /** cached converted values */
     final com.android.dx.rop.type.Type ropType;
     final CstType constant;
 
-    /** declared state */
-    private boolean declared;
-    private int flags;
-    private Type<?> supertype;
-    private String sourceFile;
-    private TypeList interfaces;
+    Type(com.android.dx.rop.type.Type ropType) {
+        this(ropType.getDescriptor(), ropType);
+    }
 
-    /** declared members */
-    private final Canonicalizer<Field<T, ?>> fields = new Canonicalizer<Field<T, ?>>();
-    private final Canonicalizer<Method<T, ?>> methods = new Canonicalizer<Method<T, ?>>();
-
-    Type(DexGenerator generator, String name) {
-        if (name == null) {
+    Type(String name, com.android.dx.rop.type.Type ropType) {
+        if (name == null || ropType == null) {
             throw new NullPointerException();
         }
-        this.generator = generator;
         this.name = name;
-        this.ropType = com.android.dx.rop.type.Type.internReturnType(name);
+        this.ropType = ropType;
         this.constant = CstType.intern(ropType);
     }
 
-    Type(DexGenerator generator, Class<T> type) {
-        this(generator, getTypeName(type));
+    /**
+     * @param name a descriptor like "Ljava/lang/Class;".
+     */
+    public static <T> Type<T> get(String name) {
+        return new Type<T>(name, com.android.dx.rop.type.Type.internReturnType(name));
+    }
+
+    public static <T> Type<T> get(Class<T> type) {
+        if (type.isPrimitive()) {
+            @SuppressWarnings("unchecked") // guarded by equals
+            Type<T> result = (Type<T>) PRIMITIVE_TO_TYPE.get(type);
+            return result;
+        }
+        String name = type.getName().replace('.', '/');
+        return get(type.isArray() ? name : 'L' + name + ';');
+    }
+
+    public <V> FieldId<T, V> getField(Type<V> type, String name) {
+        return new FieldId<T, V>(this, type, name);
+    }
+
+    public MethodId<T, Void> getConstructor(Type<?>... parameters) {
+        return new MethodId<T, Void>(this, VOID, "<init>", new TypeList(parameters));
+    }
+
+    public <R> MethodId<T, R> getMethod(Type<R> returnType, String name, Type<?>... parameters) {
+        return new MethodId<T, R>(this, returnType, name, new TypeList(parameters));
     }
 
     public String getName() {
         return name;
     }
 
-    public <R> Field<T, R> getField(Type<R> type, String name) {
-        return fields.get(new Field<T, R>(this, type, name));
-    }
-
-    public <R> Method<T, R> getMethod(Type<R> returnType, String name, Type... parameters) {
-        return methods.get(new Method<T, R>(this, returnType, name, new TypeList(parameters)));
-    }
-
-    public Method<T, Void> getConstructor(Type... parameters) {
-        return getMethod(generator.getType(void.class), "<init>", parameters);
-    }
-
-    Set<Field<T, ?>> getFields() {
-        return fields;
-    }
-
-    Set<Method<T, ?>> getMethods() {
-        return methods;
-    }
-
-    /**
-     * @param flags any flags masked by {@link com.android.dx.rop.code.AccessFlags#METHOD_FLAGS}.
-     */
-    public void declare(String sourceFile, int flags, Type<?> supertype, Type<?>... interfaces) {
-        if (declared) {
-            throw new IllegalStateException("already declared: " + this);
-        }
-        this.declared = true;
-        this.flags = flags;
-        this.supertype = supertype;
-        this.sourceFile = sourceFile;
-        this.interfaces = new TypeList(interfaces);
-    }
-
-    boolean isVoid() {
-        return constant.getClassType() == com.android.dx.rop.type.Type.VOID;
-    }
-
-    boolean isDeclared() {
-        return declared;
-    }
-
-    ClassDefItem toClassDefItem() {
-        if (!declared) {
-            throw new IllegalStateException();
-        }
-
-        DexOptions dexOptions = new DexOptions();
-        dexOptions.enableExtendedOpcodes = false;
-
-        CstType thisType = constant;
-
-        ClassDefItem out = new ClassDefItem(thisType, flags, supertype.constant,
-                interfaces.ropTypes, new CstUtf8(sourceFile));
-
-        for (Method<T, ?> method : methods) {
-            EncodedMethod encoded = method.toEncodedMethod(dexOptions);
-            if (method.isDirect()) {
-                out.addDirectMethod(encoded);
-            } else {
-                out.addVirtualMethod(encoded);
-            }
-        }
-        for (Field<T, ?> field : fields) {
-            EncodedField encoded = field.toEncodedField();
-            if (field.isStatic()) {
-                out.addStaticField(encoded, Constants.getConstant(field.getStaticValue()));
-            } else {
-                out.addInstanceField(encoded);
-            }
-        }
-
-        return out;
-    }
-
-    com.android.dx.rop.type.Type getRopType() {
-        return com.android.dx.rop.type.Type.internReturnType(name);
-    }
-
     @Override public boolean equals(Object o) {
         return o instanceof Type
                 && ((Type) o).name.equals(name);
@@ -171,15 +137,4 @@
     @Override public String toString() {
         return name;
     }
-
-    /**
-     * Returns a type name like "Ljava/lang/Integer;".
-     */
-    static String getTypeName(Class<?> type) {
-        if (type.isPrimitive()) {
-            return PRIMITIVE_TO_TYPE_NAME.get(type);
-        }
-        String name = type.getName().replace('.', '/');
-        return type.isArray() ? name : 'L' + name + ';';
-    }
 }