Home | History | Annotate | Download | only in dexmaker
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.dexmaker;
     18 
     19 import java.io.File;
     20 import java.io.FilenameFilter;
     21 import java.io.IOException;
     22 import java.lang.reflect.Field;
     23 import java.lang.reflect.InvocationTargetException;
     24 import java.lang.reflect.Method;
     25 import java.lang.reflect.Modifier;
     26 import static java.lang.reflect.Modifier.ABSTRACT;
     27 import static java.lang.reflect.Modifier.FINAL;
     28 import static java.lang.reflect.Modifier.NATIVE;
     29 import static java.lang.reflect.Modifier.PRIVATE;
     30 import static java.lang.reflect.Modifier.PROTECTED;
     31 import static java.lang.reflect.Modifier.PUBLIC;
     32 import static java.lang.reflect.Modifier.STATIC;
     33 import static java.lang.reflect.Modifier.SYNCHRONIZED;
     34 import java.util.Arrays;
     35 import java.util.List;
     36 import java.util.concurrent.Callable;
     37 import junit.framework.TestCase;
     38 
     39 /**
     40  * This generates a class named 'Generated' with one or more generated methods
     41  * and fields. In loads the generated class into the current VM and uses
     42  * reflection to invoke its methods.
     43  *
     44  * <p>This test must run on a Dalvik VM.
     45  */
     46 public final class DexMakerTest extends TestCase {
     47     private DexMaker dexMaker;
     48     private static TypeId<DexMakerTest> TEST_TYPE = TypeId.get(DexMakerTest.class);
     49     private static TypeId<?> INT_ARRAY = TypeId.get(int[].class);
     50     private static TypeId<boolean[]> BOOLEAN_ARRAY = TypeId.get(boolean[].class);
     51     private static TypeId<long[]> LONG_ARRAY = TypeId.get(long[].class);
     52     private static TypeId<Object[]> OBJECT_ARRAY = TypeId.get(Object[].class);
     53     private static TypeId<long[][]> LONG_2D_ARRAY = TypeId.get(long[][].class);
     54     private static TypeId<?> GENERATED = TypeId.get("LGenerated;");
     55     private static TypeId<Callable> CALLABLE = TypeId.get(Callable.class);
     56     private static MethodId<Callable, Object> CALL = CALLABLE.getMethod(TypeId.OBJECT, "call");
     57 
     58     @Override protected void setUp() throws Exception {
     59         super.setUp();
     60         reset();
     61     }
     62 
     63     /**
     64      * The generator is mutable. Calling reset creates a new empty generator.
     65      * This is necessary to generate multiple classes in the same test method.
     66      */
     67     private void reset() {
     68         dexMaker = new DexMaker();
     69         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
     70         clearDataDirectory();
     71     }
     72 
     73     private void clearDataDirectory() {
     74         for (File f : getDataDirectory().listFiles()) {
     75             if (f.getName().endsWith(".jar") || f.getName().endsWith(".dex")) {
     76                 f.delete();
     77             }
     78         }
     79     }
     80 
     81     public void testNewInstance() throws Exception {
     82         /*
     83          * public static Constructable call(long a, boolean b) {
     84          *   Constructable result = new Constructable(a, b);
     85          *   return result;
     86          * }
     87          */
     88         TypeId<Constructable> constructable = TypeId.get(Constructable.class);
     89         MethodId<?, Constructable> methodId = GENERATED.getMethod(
     90                 constructable, "call", TypeId.LONG, TypeId.BOOLEAN);
     91         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
     92         Local<Long> localA = code.getParameter(0, TypeId.LONG);
     93         Local<Boolean> localB = code.getParameter(1, TypeId.BOOLEAN);
     94         MethodId<Constructable, Void> constructor
     95                 = constructable.getConstructor(TypeId.LONG, TypeId.BOOLEAN);
     96         Local<Constructable> localResult = code.newLocal(constructable);
     97         code.newInstance(localResult, constructor, localA, localB);
     98         code.returnValue(localResult);
     99 
    100         Constructable constructed = (Constructable) getMethod().invoke(null, 5L, false);
    101         assertEquals(5L, constructed.a);
    102         assertEquals(false, constructed.b);
    103     }
    104 
    105     public static class Constructable {
    106         private final long a;
    107         private final boolean b;
    108         public Constructable(long a, boolean b) {
    109             this.a = a;
    110             this.b = b;
    111         }
    112     }
    113 
    114     public void testVoidNoArgMemberMethod() throws Exception {
    115         /*
    116          * public void call() {
    117          * }
    118          */
    119         MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
    120         Code code = dexMaker.declare(methodId, PUBLIC);
    121         code.returnVoid();
    122 
    123         addDefaultConstructor();
    124 
    125         Class<?> generatedClass = generateAndLoad();
    126         Object instance = generatedClass.newInstance();
    127         Method method = generatedClass.getMethod("call");
    128         method.invoke(instance);
    129     }
    130 
    131     public void testInvokeStatic() throws Exception {
    132         /*
    133          * public static int call(int a) {
    134          *   int result = DexMakerTest.staticMethod(a);
    135          *   return result;
    136          * }
    137          */
    138         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT);
    139         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    140         Local<Integer> localA = code.getParameter(0, TypeId.INT);
    141         Local<Integer> localResult = code.newLocal(TypeId.INT);
    142         MethodId<?, Integer> staticMethod
    143                 = TEST_TYPE.getMethod(TypeId.INT, "staticMethod", TypeId.INT);
    144         code.invokeStatic(staticMethod, localResult, localA);
    145         code.returnValue(localResult);
    146 
    147         assertEquals(10, getMethod().invoke(null, 4));
    148     }
    149 
    150     public void testCreateLocalMethodAsNull() throws Exception {
    151         /*
    152          * public void call(int value) {
    153          *   Method method = null;
    154          * }
    155          */
    156         MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call", TypeId.INT);
    157         TypeId<Method> methodType = TypeId.get(Method.class);
    158         Code code = dexMaker.declare(methodId, PUBLIC);
    159         Local<Method> localMethod = code.newLocal(methodType);
    160         code.loadConstant(localMethod, null);
    161         code.returnVoid();
    162 
    163         addDefaultConstructor();
    164 
    165         Class<?> generatedClass = generateAndLoad();
    166         Object instance = generatedClass.newInstance();
    167         Method method = generatedClass.getMethod("call", int.class);
    168         method.invoke(instance, 0);
    169     }
    170 
    171     @SuppressWarnings("unused") // called by generated code
    172     public static int staticMethod(int a) {
    173         return a + 6;
    174     }
    175 
    176     public void testInvokeVirtual() throws Exception {
    177         /*
    178          * public static int call(DexMakerTest test, int a) {
    179          *   int result = test.virtualMethod(a);
    180          *   return result;
    181          * }
    182          */
    183         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TEST_TYPE, TypeId.INT);
    184         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    185         Local<DexMakerTest> localInstance = code.getParameter(0, TEST_TYPE);
    186         Local<Integer> localA = code.getParameter(1, TypeId.INT);
    187         Local<Integer> localResult = code.newLocal(TypeId.INT);
    188         MethodId<DexMakerTest, Integer> virtualMethod
    189                 = TEST_TYPE.getMethod(TypeId.INT, "virtualMethod", TypeId.INT);
    190         code.invokeVirtual(virtualMethod, localResult, localInstance, localA);
    191         code.returnValue(localResult);
    192 
    193         assertEquals(9, getMethod().invoke(null, this, 4));
    194     }
    195 
    196     @SuppressWarnings("unused") // called by generated code
    197     public int virtualMethod(int a) {
    198         return a + 5;
    199     }
    200 
    201     public <G> void testInvokeDirect() throws Exception {
    202         /*
    203          * private int directMethod() {
    204          *   int a = 5;
    205          *   return a;
    206          * }
    207          *
    208          * public static int call(Generated g) {
    209          *   int b = g.directMethod();
    210          *   return b;
    211          * }
    212          */
    213         TypeId<G> generated = TypeId.get("LGenerated;");
    214         MethodId<G, Integer> directMethodId = generated.getMethod(TypeId.INT, "directMethod");
    215         Code directCode = dexMaker.declare(directMethodId, PRIVATE);
    216         directCode.getThis(generated); // 'this' is unused
    217         Local<Integer> localA = directCode.newLocal(TypeId.INT);
    218         directCode.loadConstant(localA, 5);
    219         directCode.returnValue(localA);
    220 
    221         MethodId<G, Integer> methodId = generated.getMethod(TypeId.INT, "call", generated);
    222         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    223         Local<Integer> localB = code.newLocal(TypeId.INT);
    224         Local<G> localG = code.getParameter(0, generated);
    225         code.invokeDirect(directMethodId, localB, localG);
    226         code.returnValue(localB);
    227 
    228         addDefaultConstructor();
    229 
    230         Class<?> generatedClass = generateAndLoad();
    231         Object instance = generatedClass.newInstance();
    232         Method method = generatedClass.getMethod("call", generatedClass);
    233         assertEquals(5, method.invoke(null, instance));
    234     }
    235 
    236     public <G> void testInvokeSuper() throws Exception {
    237         /*
    238          * public int superHashCode() {
    239          *   int result = super.hashCode();
    240          *   return result;
    241          * }
    242          * public int hashCode() {
    243          *   return 0;
    244          * }
    245          */
    246         TypeId<G> generated = TypeId.get("LGenerated;");
    247         MethodId<Object, Integer> objectHashCode = TypeId.OBJECT.getMethod(TypeId.INT, "hashCode");
    248         Code superHashCode = dexMaker.declare(
    249                 GENERATED.getMethod(TypeId.INT, "superHashCode"), PUBLIC);
    250         Local<Integer> localResult = superHashCode.newLocal(TypeId.INT);
    251         Local<G> localThis = superHashCode.getThis(generated);
    252         superHashCode.invokeSuper(objectHashCode, localResult, localThis);
    253         superHashCode.returnValue(localResult);
    254 
    255         Code generatedHashCode = dexMaker.declare(
    256                 GENERATED.getMethod(TypeId.INT, "hashCode"), PUBLIC);
    257         Local<Integer> localZero = generatedHashCode.newLocal(TypeId.INT);
    258         generatedHashCode.loadConstant(localZero, 0);
    259         generatedHashCode.returnValue(localZero);
    260 
    261         addDefaultConstructor();
    262 
    263         Class<?> generatedClass = generateAndLoad();
    264         Object instance = generatedClass.newInstance();
    265         Method method = generatedClass.getMethod("superHashCode");
    266         assertEquals(System.identityHashCode(instance), method.invoke(instance));
    267     }
    268 
    269     public void testInvokeInterface() throws Exception {
    270         /*
    271          * public static Object call(Callable c) {
    272          *   Object result = c.call();
    273          *   return result;
    274          * }
    275          */
    276         MethodId<?, Object> methodId = GENERATED.getMethod(TypeId.OBJECT, "call", CALLABLE);
    277         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    278         Local<Callable> localC = code.getParameter(0, CALLABLE);
    279         Local<Object> localResult = code.newLocal(TypeId.OBJECT);
    280         code.invokeInterface(CALL, localResult, localC);
    281         code.returnValue(localResult);
    282 
    283         Callable<Object> callable = new Callable<Object>() {
    284             public Object call() throws Exception {
    285                 return "abc";
    286             }
    287         };
    288         assertEquals("abc", getMethod().invoke(null, callable));
    289     }
    290 
    291     public void testInvokeVoidMethodIgnoresTargetLocal() throws Exception {
    292         /*
    293          * public static int call() {
    294          *   int result = 5;
    295          *   DexMakerTest.voidMethod();
    296          *   return result;
    297          * }
    298          */
    299         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call");
    300         MethodId<?, Void> voidMethod = TEST_TYPE.getMethod(TypeId.VOID, "voidMethod");
    301         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    302         Local<Integer> result = code.newLocal(TypeId.INT);
    303         code.loadConstant(result, 5);
    304         code.invokeStatic(voidMethod, null);
    305         code.returnValue(result);
    306 
    307         assertEquals(5, getMethod().invoke(null));
    308     }
    309 
    310     @SuppressWarnings("unused") // called by generated code
    311     public static void voidMethod() {
    312     }
    313 
    314     public void testParameterMismatch() throws Exception {
    315         TypeId<?>[] argTypes = {
    316                 TypeId.get(Integer.class), // should fail because the code specifies int
    317                 TypeId.OBJECT,
    318         };
    319         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", argTypes);
    320         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    321         try {
    322             code.getParameter(0, TypeId.INT);
    323         } catch (IllegalArgumentException e) {
    324         }
    325         try {
    326             code.getParameter(2, TypeId.INT);
    327         } catch (IndexOutOfBoundsException e) {
    328         }
    329     }
    330 
    331     public void testInvokeTypeSafety() throws Exception {
    332         /*
    333          * public static boolean call(DexMakerTest test) {
    334          *   CharSequence cs = test.toString();
    335          *   boolean result = cs.equals(test);
    336          *   return result;
    337          * }
    338          */
    339         MethodId<?, Boolean> methodId = GENERATED.getMethod(TypeId.BOOLEAN, "call", TEST_TYPE);
    340         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    341         Local<DexMakerTest> localTest = code.getParameter(0, TEST_TYPE);
    342         TypeId<CharSequence> charSequenceType = TypeId.get(CharSequence.class);
    343         MethodId<Object, String> objectToString
    344                 = TypeId.OBJECT.getMethod(TypeId.STRING, "toString");
    345         MethodId<Object, Boolean> objectEquals
    346                 = TypeId.OBJECT.getMethod(TypeId.BOOLEAN, "equals", TypeId.OBJECT);
    347         Local<CharSequence> localCs = code.newLocal(charSequenceType);
    348         Local<Boolean> localResult = code.newLocal(TypeId.BOOLEAN);
    349         code.invokeVirtual(objectToString, localCs, localTest);
    350         code.invokeVirtual(objectEquals, localResult, localCs, localTest);
    351         code.returnValue(localResult);
    352 
    353         assertEquals(false, getMethod().invoke(null, this));
    354     }
    355 
    356     public void testReturnTypeMismatch() {
    357         MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call");
    358         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    359         try {
    360             code.returnValue(code.newLocal(TypeId.BOOLEAN));
    361             fail();
    362         } catch (IllegalArgumentException expected) {
    363         }
    364         try {
    365             code.returnVoid();
    366             fail();
    367         } catch (IllegalArgumentException expected) {
    368         }
    369     }
    370 
    371     public void testDeclareStaticFields() throws Exception {
    372         /*
    373          * class Generated {
    374          *   public static int a;
    375          *   protected static Object b;
    376          * }
    377          */
    378         dexMaker.declare(GENERATED.getField(TypeId.INT, "a"), PUBLIC | STATIC, 3);
    379         dexMaker.declare(GENERATED.getField(TypeId.OBJECT, "b"), PROTECTED | STATIC, null);
    380         Class<?> generatedClass = generateAndLoad();
    381 
    382         Field a = generatedClass.getField("a");
    383         assertEquals(int.class, a.getType());
    384         assertEquals(3, a.get(null));
    385 
    386         Field b = generatedClass.getDeclaredField("b");
    387         assertEquals(Object.class, b.getType());
    388         b.setAccessible(true);
    389         assertEquals(null, b.get(null));
    390     }
    391 
    392     public void testDeclareInstanceFields() throws Exception {
    393         /*
    394          * class Generated {
    395          *   public int a;
    396          *   protected Object b;
    397          * }
    398          */
    399         dexMaker.declare(GENERATED.getField(TypeId.INT, "a"), PUBLIC, null);
    400         dexMaker.declare(GENERATED.getField(TypeId.OBJECT, "b"), PROTECTED, null);
    401 
    402         addDefaultConstructor();
    403 
    404         Class<?> generatedClass = generateAndLoad();
    405         Object instance = generatedClass.newInstance();
    406 
    407         Field a = generatedClass.getField("a");
    408         assertEquals(int.class, a.getType());
    409         assertEquals(0, a.get(instance));
    410 
    411         Field b = generatedClass.getDeclaredField("b");
    412         assertEquals(Object.class, b.getType());
    413         b.setAccessible(true);
    414         assertEquals(null, b.get(instance));
    415     }
    416 
    417     /**
    418      * Declare a constructor that takes an int parameter and assigns it to a
    419      * field.
    420      */
    421     public <G> void testDeclareConstructor() throws Exception {
    422         /*
    423          * class Generated {
    424          *   public final int a;
    425          *   public Generated(int a) {
    426          *     this.a = a;
    427          *   }
    428          * }
    429          */
    430         TypeId<G> generated = TypeId.get("LGenerated;");
    431         FieldId<G, Integer> fieldId = generated.getField(TypeId.INT, "a");
    432         dexMaker.declare(fieldId, PUBLIC | FINAL, null);
    433         MethodId<?, Void> constructor = GENERATED.getConstructor(TypeId.INT);
    434         Code code = dexMaker.declare(constructor, PUBLIC);
    435         Local<G> thisRef = code.getThis(generated);
    436         Local<Integer> parameter = code.getParameter(0, TypeId.INT);
    437         code.invokeDirect(TypeId.OBJECT.getConstructor(), null, thisRef);
    438         code.iput(fieldId, thisRef, parameter);
    439         code.returnVoid();
    440 
    441         Class<?> generatedClass = generateAndLoad();
    442         Field a = generatedClass.getField("a");
    443         Object instance = generatedClass.getConstructor(int.class).newInstance(0xabcd);
    444         assertEquals(0xabcd, a.get(instance));
    445     }
    446 
    447     public void testReturnType() throws Exception {
    448         testReturnType(boolean.class, true);
    449         testReturnType(byte.class, (byte) 5);
    450         testReturnType(char.class, 'E');
    451         testReturnType(double.class, 5.0);
    452         testReturnType(float.class, 5.0f);
    453         testReturnType(int.class, 5);
    454         testReturnType(long.class, 5L);
    455         testReturnType(short.class, (short) 5);
    456         testReturnType(void.class, null);
    457         testReturnType(String.class, "foo");
    458         testReturnType(Class.class, List.class);
    459     }
    460 
    461     private <T> void testReturnType(Class<T> javaType, T value) throws Exception {
    462         /*
    463          * public int call() {
    464          *   int a = 5;
    465          *   return a;
    466          * }
    467          */
    468         reset();
    469         TypeId<T> returnType = TypeId.get(javaType);
    470         Code code = dexMaker.declare(GENERATED.getMethod(returnType, "call"), PUBLIC | STATIC);
    471         if (value != null) {
    472             Local<T> i = code.newLocal(returnType);
    473             code.loadConstant(i, value);
    474             code.returnValue(i);
    475         } else {
    476             code.returnVoid();
    477         }
    478 
    479         Class<?> generatedClass = generateAndLoad();
    480         Method method = generatedClass.getMethod("call");
    481         assertEquals(javaType, method.getReturnType());
    482         assertEquals(value, method.invoke(null));
    483     }
    484 
    485     public void testBranching() throws Exception {
    486         Method lt = branchingMethod(Comparison.LT);
    487         assertEquals(Boolean.TRUE, lt.invoke(null, 1, 2));
    488         assertEquals(Boolean.FALSE, lt.invoke(null, 1, 1));
    489         assertEquals(Boolean.FALSE, lt.invoke(null, 2, 1));
    490 
    491         Method le = branchingMethod(Comparison.LE);
    492         assertEquals(Boolean.TRUE, le.invoke(null, 1, 2));
    493         assertEquals(Boolean.TRUE, le.invoke(null, 1, 1));
    494         assertEquals(Boolean.FALSE, le.invoke(null, 2, 1));
    495 
    496         Method eq = branchingMethod(Comparison.EQ);
    497         assertEquals(Boolean.FALSE, eq.invoke(null, 1, 2));
    498         assertEquals(Boolean.TRUE, eq.invoke(null, 1, 1));
    499         assertEquals(Boolean.FALSE, eq.invoke(null, 2, 1));
    500 
    501         Method ge = branchingMethod(Comparison.GE);
    502         assertEquals(Boolean.FALSE, ge.invoke(null, 1, 2));
    503         assertEquals(Boolean.TRUE, ge.invoke(null, 1, 1));
    504         assertEquals(Boolean.TRUE, ge.invoke(null, 2, 1));
    505 
    506         Method gt = branchingMethod(Comparison.GT);
    507         assertEquals(Boolean.FALSE, gt.invoke(null, 1, 2));
    508         assertEquals(Boolean.FALSE, gt.invoke(null, 1, 1));
    509         assertEquals(Boolean.TRUE, gt.invoke(null, 2, 1));
    510 
    511         Method ne = branchingMethod(Comparison.NE);
    512         assertEquals(Boolean.TRUE, ne.invoke(null, 1, 2));
    513         assertEquals(Boolean.FALSE, ne.invoke(null, 1, 1));
    514         assertEquals(Boolean.TRUE, ne.invoke(null, 2, 1));
    515     }
    516 
    517     private Method branchingMethod(Comparison comparison) throws Exception {
    518         /*
    519          * public static boolean call(int localA, int localB) {
    520          *   if (a comparison b) {
    521          *     return true;
    522          *   }
    523          *   return false;
    524          * }
    525          */
    526         reset();
    527         MethodId<?, Boolean> methodId = GENERATED.getMethod(
    528                 TypeId.BOOLEAN, "call", TypeId.INT, TypeId.INT);
    529         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    530         Local<Integer> localA = code.getParameter(0, TypeId.INT);
    531         Local<Integer> localB = code.getParameter(1, TypeId.INT);
    532         Local<Boolean> result = code.newLocal(TypeId.get(boolean.class));
    533         Label afterIf = new Label();
    534         Label ifBody = new Label();
    535         code.compare(comparison, ifBody, localA, localB);
    536         code.jump(afterIf);
    537 
    538         code.mark(ifBody);
    539         code.loadConstant(result, true);
    540         code.returnValue(result);
    541 
    542         code.mark(afterIf);
    543         code.loadConstant(result, false);
    544         code.returnValue(result);
    545         return getMethod();
    546     }
    547 
    548     public void testCastIntegerToInteger() throws Exception {
    549         Method intToLong = numericCastingMethod(int.class, long.class);
    550         assertEquals(0x0000000000000000L, intToLong.invoke(null, 0x00000000));
    551         assertEquals(0x000000007fffffffL, intToLong.invoke(null, 0x7fffffff));
    552         assertEquals(0xffffffff80000000L, intToLong.invoke(null, 0x80000000));
    553         assertEquals(0xffffffffffffffffL, intToLong.invoke(null, 0xffffffff));
    554 
    555         Method longToInt = numericCastingMethod(long.class, int.class);
    556         assertEquals(0x1234abcd, longToInt.invoke(null, 0x000000001234abcdL));
    557         assertEquals(0x1234abcd, longToInt.invoke(null, 0x123456781234abcdL));
    558         assertEquals(0x1234abcd, longToInt.invoke(null, 0xffffffff1234abcdL));
    559 
    560         Method intToShort = numericCastingMethod(int.class, short.class);
    561         assertEquals((short) 0x1234, intToShort.invoke(null, 0x00001234));
    562         assertEquals((short) 0x1234, intToShort.invoke(null, 0xabcd1234));
    563         assertEquals((short) 0x1234, intToShort.invoke(null, 0xffff1234));
    564 
    565         Method intToChar = numericCastingMethod(int.class, char.class);
    566         assertEquals((char) 0x1234, intToChar.invoke(null, 0x00001234));
    567         assertEquals((char) 0x1234, intToChar.invoke(null, 0xabcd1234));
    568         assertEquals((char) 0x1234, intToChar.invoke(null, 0xffff1234));
    569 
    570         Method intToByte = numericCastingMethod(int.class, byte.class);
    571         assertEquals((byte) 0x34, intToByte.invoke(null, 0x00000034));
    572         assertEquals((byte) 0x34, intToByte.invoke(null, 0xabcd1234));
    573         assertEquals((byte) 0x34, intToByte.invoke(null, 0xffffff34));
    574     }
    575 
    576     public void testCastIntegerToFloatingPoint() throws Exception {
    577         Method intToFloat = numericCastingMethod(int.class, float.class);
    578         assertEquals(0.0f, intToFloat.invoke(null, 0));
    579         assertEquals(-1.0f, intToFloat.invoke(null, -1));
    580         assertEquals(16777216f, intToFloat.invoke(null, 16777216));
    581         assertEquals(16777216f, intToFloat.invoke(null, 16777217)); // precision
    582 
    583         Method intToDouble = numericCastingMethod(int.class, double.class);
    584         assertEquals(0.0, intToDouble.invoke(null, 0));
    585         assertEquals(-1.0, intToDouble.invoke(null, -1));
    586         assertEquals(16777216.0, intToDouble.invoke(null, 16777216));
    587         assertEquals(16777217.0, intToDouble.invoke(null, 16777217));
    588 
    589         Method longToFloat = numericCastingMethod(long.class, float.class);
    590         assertEquals(0.0f, longToFloat.invoke(null, 0L));
    591         assertEquals(-1.0f, longToFloat.invoke(null, -1L));
    592         assertEquals(16777216f, longToFloat.invoke(null, 16777216L));
    593         assertEquals(16777216f, longToFloat.invoke(null, 16777217L));
    594 
    595         Method longToDouble = numericCastingMethod(long.class, double.class);
    596         assertEquals(0.0, longToDouble.invoke(null, 0L));
    597         assertEquals(-1.0, longToDouble.invoke(null, -1L));
    598         assertEquals(9007199254740992.0, longToDouble.invoke(null, 9007199254740992L));
    599         assertEquals(9007199254740992.0, longToDouble.invoke(null, 9007199254740993L)); // precision
    600     }
    601 
    602     public void testCastFloatingPointToInteger() throws Exception {
    603         Method floatToInt = numericCastingMethod(float.class, int.class);
    604         assertEquals(0, floatToInt.invoke(null, 0.0f));
    605         assertEquals(-1, floatToInt.invoke(null, -1.0f));
    606         assertEquals(Integer.MAX_VALUE, floatToInt.invoke(null, 10e15f));
    607         assertEquals(0, floatToInt.invoke(null, 0.5f));
    608         assertEquals(Integer.MIN_VALUE, floatToInt.invoke(null, Float.NEGATIVE_INFINITY));
    609         assertEquals(0, floatToInt.invoke(null, Float.NaN));
    610 
    611         Method floatToLong = numericCastingMethod(float.class, long.class);
    612         assertEquals(0L, floatToLong.invoke(null, 0.0f));
    613         assertEquals(-1L, floatToLong.invoke(null, -1.0f));
    614         assertEquals(10000000272564224L, floatToLong.invoke(null, 10e15f));
    615         assertEquals(0L, floatToLong.invoke(null, 0.5f));
    616         assertEquals(Long.MIN_VALUE, floatToLong.invoke(null, Float.NEGATIVE_INFINITY));
    617         assertEquals(0L, floatToLong.invoke(null, Float.NaN));
    618 
    619         Method doubleToInt = numericCastingMethod(double.class, int.class);
    620         assertEquals(0, doubleToInt.invoke(null, 0.0));
    621         assertEquals(-1, doubleToInt.invoke(null, -1.0));
    622         assertEquals(Integer.MAX_VALUE, doubleToInt.invoke(null, 10e15));
    623         assertEquals(0, doubleToInt.invoke(null, 0.5));
    624         assertEquals(Integer.MIN_VALUE, doubleToInt.invoke(null, Double.NEGATIVE_INFINITY));
    625         assertEquals(0, doubleToInt.invoke(null, Double.NaN));
    626 
    627         Method doubleToLong = numericCastingMethod(double.class, long.class);
    628         assertEquals(0L, doubleToLong.invoke(null, 0.0));
    629         assertEquals(-1L, doubleToLong.invoke(null, -1.0));
    630         assertEquals(10000000000000000L, doubleToLong.invoke(null, 10e15));
    631         assertEquals(0L, doubleToLong.invoke(null, 0.5));
    632         assertEquals(Long.MIN_VALUE, doubleToLong.invoke(null, Double.NEGATIVE_INFINITY));
    633         assertEquals(0L, doubleToLong.invoke(null, Double.NaN));
    634     }
    635 
    636     public void testCastFloatingPointToFloatingPoint() throws Exception {
    637         Method floatToDouble = numericCastingMethod(float.class, double.class);
    638         assertEquals(0.0, floatToDouble.invoke(null, 0.0f));
    639         assertEquals(-1.0, floatToDouble.invoke(null, -1.0f));
    640         assertEquals(0.5, floatToDouble.invoke(null, 0.5f));
    641         assertEquals(Double.NEGATIVE_INFINITY, floatToDouble.invoke(null, Float.NEGATIVE_INFINITY));
    642         assertEquals(Double.NaN, floatToDouble.invoke(null, Float.NaN));
    643 
    644         Method doubleToFloat = numericCastingMethod(double.class, float.class);
    645         assertEquals(0.0f, doubleToFloat.invoke(null, 0.0));
    646         assertEquals(-1.0f, doubleToFloat.invoke(null, -1.0));
    647         assertEquals(0.5f, doubleToFloat.invoke(null, 0.5));
    648         assertEquals(Float.NEGATIVE_INFINITY, doubleToFloat.invoke(null, Double.NEGATIVE_INFINITY));
    649         assertEquals(Float.NaN, doubleToFloat.invoke(null, Double.NaN));
    650     }
    651 
    652     private Method numericCastingMethod(Class<?> source, Class<?> target)
    653             throws Exception {
    654         /*
    655          * public static short call(int source) {
    656          *   short casted = (short) source;
    657          *   return casted;
    658          * }
    659          */
    660         reset();
    661         TypeId<?> sourceType = TypeId.get(source);
    662         TypeId<?> targetType = TypeId.get(target);
    663         MethodId<?, ?> methodId = GENERATED.getMethod(targetType, "call", sourceType);
    664         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    665         Local<?> localSource = code.getParameter(0, sourceType);
    666         Local<?> localCasted = code.newLocal(targetType);
    667         code.cast(localCasted, localSource);
    668         code.returnValue(localCasted);
    669         return getMethod();
    670     }
    671 
    672     public void testNot() throws Exception {
    673         Method notInteger = notMethod(int.class);
    674         assertEquals(0xffffffff, notInteger.invoke(null, 0x00000000));
    675         assertEquals(0x00000000, notInteger.invoke(null, 0xffffffff));
    676         assertEquals(0xedcba987, notInteger.invoke(null, 0x12345678));
    677 
    678         Method notLong = notMethod(long.class);
    679         assertEquals(0xffffffffffffffffL, notLong.invoke(null, 0x0000000000000000L));
    680         assertEquals(0x0000000000000000L, notLong.invoke(null, 0xffffffffffffffffL));
    681         assertEquals(0x98765432edcba987L, notLong.invoke(null, 0x6789abcd12345678L));
    682     }
    683 
    684     private <T> Method notMethod(Class<T> source) throws Exception {
    685         /*
    686          * public static short call(int source) {
    687          *   source = ~source;
    688          *   return not;
    689          * }
    690          */
    691         reset();
    692         TypeId<T> valueType = TypeId.get(source);
    693         MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType);
    694         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    695         Local<T> localSource = code.getParameter(0, valueType);
    696         code.op(UnaryOp.NOT, localSource, localSource);
    697         code.returnValue(localSource);
    698         return getMethod();
    699     }
    700 
    701     public void testNegate() throws Exception {
    702         Method negateInteger = negateMethod(int.class);
    703         assertEquals(0, negateInteger.invoke(null, 0));
    704         assertEquals(-1, negateInteger.invoke(null, 1));
    705         assertEquals(Integer.MIN_VALUE, negateInteger.invoke(null, Integer.MIN_VALUE));
    706 
    707         Method negateLong = negateMethod(long.class);
    708         assertEquals(0L, negateLong.invoke(null, 0));
    709         assertEquals(-1L, negateLong.invoke(null, 1));
    710         assertEquals(Long.MIN_VALUE, negateLong.invoke(null, Long.MIN_VALUE));
    711 
    712         Method negateFloat = negateMethod(float.class);
    713         assertEquals(-0.0f, negateFloat.invoke(null, 0.0f));
    714         assertEquals(-1.0f, negateFloat.invoke(null, 1.0f));
    715         assertEquals(Float.NaN, negateFloat.invoke(null, Float.NaN));
    716         assertEquals(Float.POSITIVE_INFINITY, negateFloat.invoke(null, Float.NEGATIVE_INFINITY));
    717 
    718         Method negateDouble = negateMethod(double.class);
    719         assertEquals(-0.0, negateDouble.invoke(null, 0.0));
    720         assertEquals(-1.0, negateDouble.invoke(null, 1.0));
    721         assertEquals(Double.NaN, negateDouble.invoke(null, Double.NaN));
    722         assertEquals(Double.POSITIVE_INFINITY, negateDouble.invoke(null, Double.NEGATIVE_INFINITY));
    723     }
    724 
    725     private <T> Method negateMethod(Class<T> source) throws Exception {
    726         /*
    727          * public static short call(int source) {
    728          *   source = -source;
    729          *   return not;
    730          * }
    731          */
    732         reset();
    733         TypeId<T> valueType = TypeId.get(source);
    734         MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType);
    735         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    736         Local<T> localSource = code.getParameter(0, valueType);
    737         code.op(UnaryOp.NEGATE, localSource, localSource);
    738         code.returnValue(localSource);
    739         return getMethod();
    740     }
    741 
    742     public void testIntBinaryOps() throws Exception {
    743         Method add = binaryOpMethod(int.class, int.class, BinaryOp.ADD);
    744         assertEquals(79, add.invoke(null, 75, 4));
    745 
    746         Method subtract = binaryOpMethod(int.class, int.class, BinaryOp.SUBTRACT);
    747         assertEquals(71, subtract.invoke(null, 75, 4));
    748 
    749         Method multiply = binaryOpMethod(int.class, int.class, BinaryOp.MULTIPLY);
    750         assertEquals(300, multiply.invoke(null, 75, 4));
    751 
    752         Method divide = binaryOpMethod(int.class, int.class, BinaryOp.DIVIDE);
    753         assertEquals(18, divide.invoke(null, 75, 4));
    754         try {
    755             divide.invoke(null, 75, 0);
    756             fail();
    757         } catch (InvocationTargetException expected) {
    758             assertEquals(ArithmeticException.class, expected.getCause().getClass());
    759         }
    760 
    761         Method remainder = binaryOpMethod(int.class, int.class, BinaryOp.REMAINDER);
    762         assertEquals(3, remainder.invoke(null, 75, 4));
    763         try {
    764             remainder.invoke(null, 75, 0);
    765             fail();
    766         } catch (InvocationTargetException expected) {
    767             assertEquals(ArithmeticException.class, expected.getCause().getClass());
    768         }
    769 
    770         Method and = binaryOpMethod(int.class, int.class, BinaryOp.AND);
    771         assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000));
    772 
    773         Method or = binaryOpMethod(int.class, int.class, BinaryOp.OR);
    774         assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000));
    775 
    776         Method xor = binaryOpMethod(int.class, int.class, BinaryOp.XOR);
    777         assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000));
    778 
    779         Method shiftLeft = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_LEFT);
    780         assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8));
    781 
    782         Method shiftRight = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_RIGHT);
    783         assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8));
    784 
    785         Method unsignedShiftRight = binaryOpMethod(int.class,
    786                 int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT);
    787         assertEquals(0x00abcd12, unsignedShiftRight.invoke(null, 0xabcd1234, 8));
    788     }
    789 
    790     public void testLongBinaryOps() throws Exception {
    791         Method add = binaryOpMethod(long.class, long.class, BinaryOp.ADD);
    792         assertEquals(30000000079L, add.invoke(null, 10000000075L, 20000000004L));
    793 
    794         Method subtract = binaryOpMethod(long.class, long.class, BinaryOp.SUBTRACT);
    795         assertEquals(20000000071L, subtract.invoke(null, 30000000075L, 10000000004L));
    796 
    797         Method multiply = binaryOpMethod(long.class, long.class, BinaryOp.MULTIPLY);
    798         assertEquals(-8742552812415203028L, multiply.invoke(null, 30000000075L, 20000000004L));
    799 
    800         Method divide = binaryOpMethod(long.class, long.class, BinaryOp.DIVIDE);
    801         assertEquals(-2L, divide.invoke(null, -8742552812415203028L, 4142552812415203028L));
    802         try {
    803             divide.invoke(null, -8742552812415203028L, 0L);
    804             fail();
    805         } catch (InvocationTargetException expected) {
    806             assertEquals(ArithmeticException.class, expected.getCause().getClass());
    807         }
    808 
    809         Method remainder = binaryOpMethod(long.class, long.class, BinaryOp.REMAINDER);
    810         assertEquals(10000000004L, remainder.invoke(null, 30000000079L, 20000000075L));
    811         try {
    812             remainder.invoke(null, 30000000079L, 0L);
    813             fail();
    814         } catch (InvocationTargetException expected) {
    815             assertEquals(ArithmeticException.class, expected.getCause().getClass());
    816         }
    817 
    818         Method and = binaryOpMethod(long.class, long.class, BinaryOp.AND);
    819         assertEquals(0xff00ff0000000000L,
    820                 and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
    821 
    822         Method or = binaryOpMethod(long.class, long.class, BinaryOp.OR);
    823         assertEquals(0xffffffffff00ff00L,
    824                 or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
    825 
    826         Method xor = binaryOpMethod(long.class, long.class, BinaryOp.XOR);
    827         assertEquals(0x00ff00ffff00ff00L,
    828                 xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
    829 
    830         Method shiftLeft = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_LEFT);
    831         assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8));
    832 
    833         Method shiftRight = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_RIGHT);
    834         assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8));
    835 
    836         Method unsignedShiftRight = binaryOpMethod(
    837                 long.class, int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT);
    838         assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8));
    839     }
    840 
    841     public void testFloatBinaryOps() throws Exception {
    842         Method add = binaryOpMethod(float.class, float.class, BinaryOp.ADD);
    843         assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f));
    844 
    845         Method subtract = binaryOpMethod(float.class, float.class, BinaryOp.SUBTRACT);
    846         assertEquals(4.25f, subtract.invoke(null, 5.5f, 1.25f));
    847 
    848         Method multiply = binaryOpMethod(float.class, float.class, BinaryOp.MULTIPLY);
    849         assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f));
    850 
    851         Method divide = binaryOpMethod(float.class, float.class, BinaryOp.DIVIDE);
    852         assertEquals(4.4f, divide.invoke(null, 5.5f, 1.25f));
    853         assertEquals(Float.POSITIVE_INFINITY, divide.invoke(null, 5.5f, 0.0f));
    854 
    855         Method remainder = binaryOpMethod(float.class, float.class, BinaryOp.REMAINDER);
    856         assertEquals(0.5f, remainder.invoke(null, 5.5f, 1.25f));
    857         assertEquals(Float.NaN, remainder.invoke(null, 5.5f, 0.0f));
    858     }
    859 
    860     public void testDoubleBinaryOps() throws Exception {
    861         Method add = binaryOpMethod(double.class, double.class, BinaryOp.ADD);
    862         assertEquals(6.75, add.invoke(null, 5.5, 1.25));
    863 
    864         Method subtract = binaryOpMethod(double.class, double.class, BinaryOp.SUBTRACT);
    865         assertEquals(4.25, subtract.invoke(null, 5.5, 1.25));
    866 
    867         Method multiply = binaryOpMethod(double.class, double.class, BinaryOp.MULTIPLY);
    868         assertEquals(6.875, multiply.invoke(null, 5.5, 1.25));
    869 
    870         Method divide = binaryOpMethod(double.class, double.class, BinaryOp.DIVIDE);
    871         assertEquals(4.4, divide.invoke(null, 5.5, 1.25));
    872         assertEquals(Double.POSITIVE_INFINITY, divide.invoke(null, 5.5, 0.0));
    873 
    874         Method remainder = binaryOpMethod(double.class, double.class, BinaryOp.REMAINDER);
    875         assertEquals(0.5, remainder.invoke(null, 5.5, 1.25));
    876         assertEquals(Double.NaN, remainder.invoke(null, 5.5, 0.0));
    877     }
    878 
    879     private <T1, T2> Method binaryOpMethod(
    880             Class<T1> valueAClass, Class<T2> valueBClass, BinaryOp op) throws Exception {
    881         /*
    882          * public static int binaryOp(int a, int b) {
    883          *   int result = a + b;
    884          *   return result;
    885          * }
    886          */
    887         reset();
    888         TypeId<T1> valueAType = TypeId.get(valueAClass);
    889         TypeId<T2> valueBType = TypeId.get(valueBClass);
    890         MethodId<?, T1> methodId = GENERATED.getMethod(valueAType, "call", valueAType, valueBType);
    891         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    892         Local<T1> localA = code.getParameter(0, valueAType);
    893         Local<T2> localB = code.getParameter(1, valueBType);
    894         Local<T1> localResult = code.newLocal(valueAType);
    895         code.op(op, localResult, localA, localB);
    896         code.returnValue(localResult);
    897         return getMethod();
    898     }
    899 
    900     public void testReadAndWriteInstanceFields() throws Exception {
    901         Instance instance = new Instance();
    902 
    903         Method intSwap = instanceSwapMethod(int.class, "intValue");
    904         instance.intValue = 5;
    905         assertEquals(5, intSwap.invoke(null, instance, 10));
    906         assertEquals(10, instance.intValue);
    907 
    908         Method longSwap = instanceSwapMethod(long.class, "longValue");
    909         instance.longValue = 500L;
    910         assertEquals(500L, longSwap.invoke(null, instance, 1234L));
    911         assertEquals(1234L, instance.longValue);
    912 
    913         Method booleanSwap = instanceSwapMethod(boolean.class, "booleanValue");
    914         instance.booleanValue = false;
    915         assertEquals(false, booleanSwap.invoke(null, instance, true));
    916         assertEquals(true, instance.booleanValue);
    917 
    918         Method floatSwap = instanceSwapMethod(float.class, "floatValue");
    919         instance.floatValue = 1.5f;
    920         assertEquals(1.5f, floatSwap.invoke(null, instance, 0.5f));
    921         assertEquals(0.5f, instance.floatValue);
    922 
    923         Method doubleSwap = instanceSwapMethod(double.class, "doubleValue");
    924         instance.doubleValue = 155.5;
    925         assertEquals(155.5, doubleSwap.invoke(null, instance, 266.6));
    926         assertEquals(266.6, instance.doubleValue);
    927 
    928         Method objectSwap = instanceSwapMethod(Object.class, "objectValue");
    929         instance.objectValue = "before";
    930         assertEquals("before", objectSwap.invoke(null, instance, "after"));
    931         assertEquals("after", instance.objectValue);
    932 
    933         Method byteSwap = instanceSwapMethod(byte.class, "byteValue");
    934         instance.byteValue = 0x35;
    935         assertEquals((byte) 0x35, byteSwap.invoke(null, instance, (byte) 0x64));
    936         assertEquals((byte) 0x64, instance.byteValue);
    937 
    938         Method charSwap = instanceSwapMethod(char.class, "charValue");
    939         instance.charValue = 'A';
    940         assertEquals('A', charSwap.invoke(null, instance, 'B'));
    941         assertEquals('B', instance.charValue);
    942 
    943         Method shortSwap = instanceSwapMethod(short.class, "shortValue");
    944         instance.shortValue = (short) 0xabcd;
    945         assertEquals((short) 0xabcd, shortSwap.invoke(null, instance, (short) 0x1234));
    946         assertEquals((short) 0x1234, instance.shortValue);
    947     }
    948 
    949     public class Instance {
    950         public int intValue;
    951         public long longValue;
    952         public float floatValue;
    953         public double doubleValue;
    954         public Object objectValue;
    955         public boolean booleanValue;
    956         public byte byteValue;
    957         public char charValue;
    958         public short shortValue;
    959     }
    960 
    961     private <V> Method instanceSwapMethod(
    962             Class<V> valueClass, String fieldName) throws Exception {
    963         /*
    964          * public static int call(Instance instance, int newValue) {
    965          *   int oldValue = instance.intValue;
    966          *   instance.intValue = newValue;
    967          *   return oldValue;
    968          * }
    969          */
    970         reset();
    971         TypeId<V> valueType = TypeId.get(valueClass);
    972         TypeId<Instance> objectType = TypeId.get(Instance.class);
    973         FieldId<Instance, V> fieldId = objectType.getField(valueType, fieldName);
    974         MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", objectType, valueType);
    975         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
    976         Local<Instance> localInstance = code.getParameter(0, objectType);
    977         Local<V> localNewValue = code.getParameter(1, valueType);
    978         Local<V> localOldValue = code.newLocal(valueType);
    979         code.iget(fieldId, localOldValue, localInstance);
    980         code.iput(fieldId, localInstance, localNewValue);
    981         code.returnValue(localOldValue);
    982         return getMethod();
    983     }
    984 
    985     public void testReadAndWriteStaticFields() throws Exception {
    986         Method intSwap = staticSwapMethod(int.class, "intValue");
    987         Static.intValue = 5;
    988         assertEquals(5, intSwap.invoke(null, 10));
    989         assertEquals(10, Static.intValue);
    990 
    991         Method longSwap = staticSwapMethod(long.class, "longValue");
    992         Static.longValue = 500L;
    993         assertEquals(500L, longSwap.invoke(null, 1234L));
    994         assertEquals(1234L, Static.longValue);
    995 
    996         Method booleanSwap = staticSwapMethod(boolean.class, "booleanValue");
    997         Static.booleanValue = false;
    998         assertEquals(false, booleanSwap.invoke(null, true));
    999         assertEquals(true, Static.booleanValue);
   1000 
   1001         Method floatSwap = staticSwapMethod(float.class, "floatValue");
   1002         Static.floatValue = 1.5f;
   1003         assertEquals(1.5f, floatSwap.invoke(null, 0.5f));
   1004         assertEquals(0.5f, Static.floatValue);
   1005 
   1006         Method doubleSwap = staticSwapMethod(double.class, "doubleValue");
   1007         Static.doubleValue = 155.5;
   1008         assertEquals(155.5, doubleSwap.invoke(null, 266.6));
   1009         assertEquals(266.6, Static.doubleValue);
   1010 
   1011         Method objectSwap = staticSwapMethod(Object.class, "objectValue");
   1012         Static.objectValue = "before";
   1013         assertEquals("before", objectSwap.invoke(null, "after"));
   1014         assertEquals("after", Static.objectValue);
   1015 
   1016         Method byteSwap = staticSwapMethod(byte.class, "byteValue");
   1017         Static.byteValue = 0x35;
   1018         assertEquals((byte) 0x35, byteSwap.invoke(null, (byte) 0x64));
   1019         assertEquals((byte) 0x64, Static.byteValue);
   1020 
   1021         Method charSwap = staticSwapMethod(char.class, "charValue");
   1022         Static.charValue = 'A';
   1023         assertEquals('A', charSwap.invoke(null, 'B'));
   1024         assertEquals('B', Static.charValue);
   1025 
   1026         Method shortSwap = staticSwapMethod(short.class, "shortValue");
   1027         Static.shortValue = (short) 0xabcd;
   1028         assertEquals((short) 0xabcd, shortSwap.invoke(null, (short) 0x1234));
   1029         assertEquals((short) 0x1234, Static.shortValue);
   1030     }
   1031 
   1032     public static class Static {
   1033         public static int intValue;
   1034         public static long longValue;
   1035         public static float floatValue;
   1036         public static double doubleValue;
   1037         public static Object objectValue;
   1038         public static boolean booleanValue;
   1039         public static byte byteValue;
   1040         public static char charValue;
   1041         public static short shortValue;
   1042     }
   1043 
   1044     private <V> Method staticSwapMethod(Class<V> valueClass, String fieldName)
   1045             throws Exception {
   1046         /*
   1047          * public static int call(int newValue) {
   1048          *   int oldValue = Static.intValue;
   1049          *   Static.intValue = newValue;
   1050          *   return oldValue;
   1051          * }
   1052          */
   1053         reset();
   1054         TypeId<V> valueType = TypeId.get(valueClass);
   1055         TypeId<Static> objectType = TypeId.get(Static.class);
   1056         FieldId<Static, V> fieldId = objectType.getField(valueType, fieldName);
   1057         MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", valueType);
   1058         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1059         Local<V> localNewValue = code.getParameter(0, valueType);
   1060         Local<V> localOldValue = code.newLocal(valueType);
   1061         code.sget(fieldId, localOldValue);
   1062         code.sput(fieldId, localNewValue);
   1063         code.returnValue(localOldValue);
   1064         return getMethod();
   1065     }
   1066 
   1067     public void testTypeCast() throws Exception {
   1068         /*
   1069          * public static String call(Object o) {
   1070          *   String s = (String) o;
   1071          * }
   1072          */
   1073         MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call", TypeId.OBJECT);
   1074         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1075         Local<Object> localObject = code.getParameter(0, TypeId.OBJECT);
   1076         Local<String> localString = code.newLocal(TypeId.STRING);
   1077         code.cast(localString, localObject);
   1078         code.returnValue(localString);
   1079 
   1080         Method method = getMethod();
   1081         assertEquals("s", method.invoke(null, "s"));
   1082         assertEquals(null, method.invoke(null, (String) null));
   1083         try {
   1084             method.invoke(null, 5);
   1085             fail();
   1086         } catch (InvocationTargetException expected) {
   1087             assertEquals(ClassCastException.class, expected.getCause().getClass());
   1088         }
   1089     }
   1090 
   1091     public void testInstanceOf() throws Exception {
   1092         /*
   1093          * public static boolean call(Object o) {
   1094          *   boolean result = o instanceof String;
   1095          *   return result;
   1096          * }
   1097          */
   1098         MethodId<?, Boolean> methodId = GENERATED.getMethod(TypeId.BOOLEAN, "call", TypeId.OBJECT);
   1099         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1100         Local<Object> localObject = code.getParameter(0, TypeId.OBJECT);
   1101         Local<Boolean> localResult = code.newLocal(TypeId.BOOLEAN);
   1102         code.instanceOfType(localResult, localObject, TypeId.STRING);
   1103         code.returnValue(localResult);
   1104 
   1105         Method method = getMethod();
   1106         assertEquals(true, method.invoke(null, "s"));
   1107         assertEquals(false, method.invoke(null, (String) null));
   1108         assertEquals(false, method.invoke(null, 5));
   1109     }
   1110 
   1111     /**
   1112      * Tests that we can construct a for loop.
   1113      */
   1114     public void testForLoop() throws Exception {
   1115         /*
   1116          * public static int call(int count) {
   1117          *   int result = 1;
   1118          *   for (int i = 0; i < count; i += 1) {
   1119          *     result = result * 2;
   1120          *   }
   1121          *   return result;
   1122          * }
   1123          */
   1124         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT);
   1125         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1126         Local<Integer> localCount = code.getParameter(0, TypeId.INT);
   1127         Local<Integer> localResult = code.newLocal(TypeId.INT);
   1128         Local<Integer> localI = code.newLocal(TypeId.INT);
   1129         Local<Integer> local1 = code.newLocal(TypeId.INT);
   1130         Local<Integer> local2 = code.newLocal(TypeId.INT);
   1131         code.loadConstant(local1, 1);
   1132         code.loadConstant(local2, 2);
   1133         code.loadConstant(localResult, 1);
   1134         code.loadConstant(localI, 0);
   1135         Label loopCondition = new Label();
   1136         Label loopBody = new Label();
   1137         Label afterLoop = new Label();
   1138         code.mark(loopCondition);
   1139         code.compare(Comparison.LT, loopBody, localI, localCount);
   1140         code.jump(afterLoop);
   1141         code.mark(loopBody);
   1142         code.op(BinaryOp.MULTIPLY, localResult, localResult, local2);
   1143         code.op(BinaryOp.ADD, localI, localI, local1);
   1144         code.jump(loopCondition);
   1145         code.mark(afterLoop);
   1146         code.returnValue(localResult);
   1147 
   1148         Method pow2 = getMethod();
   1149         assertEquals(1, pow2.invoke(null, 0));
   1150         assertEquals(2, pow2.invoke(null, 1));
   1151         assertEquals(4, pow2.invoke(null, 2));
   1152         assertEquals(8, pow2.invoke(null, 3));
   1153         assertEquals(16, pow2.invoke(null, 4));
   1154     }
   1155 
   1156     /**
   1157      * Tests that we can construct a while loop.
   1158      */
   1159     public void testWhileLoop() throws Exception {
   1160         /*
   1161          * public static int call(int max) {
   1162          *   int result = 1;
   1163          *   while (result < max) {
   1164          *     result = result * 2;
   1165          *   }
   1166          *   return result;
   1167          * }
   1168          */
   1169         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT);
   1170         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1171         Local<Integer> localMax = code.getParameter(0, TypeId.INT);
   1172         Local<Integer> localResult = code.newLocal(TypeId.INT);
   1173         Local<Integer> local2 = code.newLocal(TypeId.INT);
   1174         code.loadConstant(localResult, 1);
   1175         code.loadConstant(local2, 2);
   1176         Label loopCondition = new Label();
   1177         Label loopBody = new Label();
   1178         Label afterLoop = new Label();
   1179         code.mark(loopCondition);
   1180         code.compare(Comparison.LT, loopBody, localResult, localMax);
   1181         code.jump(afterLoop);
   1182         code.mark(loopBody);
   1183         code.op(BinaryOp.MULTIPLY, localResult, localResult, local2);
   1184         code.jump(loopCondition);
   1185         code.mark(afterLoop);
   1186         code.returnValue(localResult);
   1187 
   1188         Method ceilPow2 = getMethod();
   1189         assertEquals(1, ceilPow2.invoke(null, 1));
   1190         assertEquals(2, ceilPow2.invoke(null, 2));
   1191         assertEquals(4, ceilPow2.invoke(null, 3));
   1192         assertEquals(16, ceilPow2.invoke(null, 10));
   1193         assertEquals(128, ceilPow2.invoke(null, 100));
   1194         assertEquals(1024, ceilPow2.invoke(null, 1000));
   1195     }
   1196 
   1197     public void testIfElseBlock() throws Exception {
   1198         /*
   1199          * public static int call(int a, int b, int c) {
   1200          *   if (a < b) {
   1201          *     if (a < c) {
   1202          *       return a;
   1203          *     } else {
   1204          *       return c;
   1205          *     }
   1206          *   } else if (b < c) {
   1207          *     return b;
   1208          *   } else {
   1209          *     return c;
   1210          *   }
   1211          * }
   1212          */
   1213         MethodId<?, Integer> methodId = GENERATED.getMethod(
   1214                 TypeId.INT, "call", TypeId.INT, TypeId.INT, TypeId.INT);
   1215         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1216         Local<Integer> localA = code.getParameter(0, TypeId.INT);
   1217         Local<Integer> localB = code.getParameter(1, TypeId.INT);
   1218         Local<Integer> localC = code.getParameter(2, TypeId.INT);
   1219         Label aLessThanB = new Label();
   1220         Label aLessThanC = new Label();
   1221         Label bLessThanC = new Label();
   1222         code.compare(Comparison.LT, aLessThanB, localA, localB);
   1223         code.compare(Comparison.LT, bLessThanC, localB, localC);
   1224         code.returnValue(localC);
   1225         // (a < b)
   1226         code.mark(aLessThanB);
   1227         code.compare(Comparison.LT, aLessThanC, localA, localC);
   1228         code.returnValue(localC);
   1229         // (a < c)
   1230         code.mark(aLessThanC);
   1231         code.returnValue(localA);
   1232         // (b < c)
   1233         code.mark(bLessThanC);
   1234         code.returnValue(localB);
   1235 
   1236         Method min = getMethod();
   1237         assertEquals(1, min.invoke(null, 1, 2, 3));
   1238         assertEquals(1, min.invoke(null, 2, 3, 1));
   1239         assertEquals(1, min.invoke(null, 2, 1, 3));
   1240         assertEquals(1, min.invoke(null, 3, 2, 1));
   1241     }
   1242 
   1243     public void testRecursion() throws Exception {
   1244         /*
   1245          * public static int call(int a) {
   1246          *   if (a < 2) {
   1247          *     return a;
   1248          *   }
   1249          *   a -= 1;
   1250          *   int x = call(a)
   1251          *   a -= 1;
   1252          *   int y = call(a);
   1253          *   int result = x + y;
   1254          *   return result;
   1255          * }
   1256          */
   1257         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT);
   1258         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1259         Local<Integer> localA = code.getParameter(0, TypeId.INT);
   1260         Local<Integer> local1 = code.newLocal(TypeId.INT);
   1261         Local<Integer> local2 = code.newLocal(TypeId.INT);
   1262         Local<Integer> localX = code.newLocal(TypeId.INT);
   1263         Local<Integer> localY = code.newLocal(TypeId.INT);
   1264         Local<Integer> localResult = code.newLocal(TypeId.INT);
   1265         Label baseCase = new Label();
   1266         code.loadConstant(local1, 1);
   1267         code.loadConstant(local2, 2);
   1268         code.compare(Comparison.LT, baseCase, localA, local2);
   1269         code.op(BinaryOp.SUBTRACT, localA, localA, local1);
   1270         code.invokeStatic(methodId, localX, localA);
   1271         code.op(BinaryOp.SUBTRACT, localA, localA, local1);
   1272         code.invokeStatic(methodId, localY, localA);
   1273         code.op(BinaryOp.ADD, localResult, localX, localY);
   1274         code.returnValue(localResult);
   1275         code.mark(baseCase);
   1276         code.returnValue(localA);
   1277 
   1278         Method fib = getMethod();
   1279         assertEquals(0, fib.invoke(null, 0));
   1280         assertEquals(1, fib.invoke(null, 1));
   1281         assertEquals(1, fib.invoke(null, 2));
   1282         assertEquals(2, fib.invoke(null, 3));
   1283         assertEquals(3, fib.invoke(null, 4));
   1284         assertEquals(5, fib.invoke(null, 5));
   1285         assertEquals(8, fib.invoke(null, 6));
   1286     }
   1287 
   1288     public void testCatchExceptions() throws Exception {
   1289         /*
   1290          * public static String call(int i) {
   1291          *   try {
   1292          *     DexMakerTest.thrower(i);
   1293          *     return "NONE";
   1294          *   } catch (IllegalArgumentException e) {
   1295          *     return "IAE";
   1296          *   } catch (IllegalStateException e) {
   1297          *     return "ISE";
   1298          *   } catch (RuntimeException e) {
   1299          *     return "RE";
   1300          *   }
   1301          */
   1302         MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call", TypeId.INT);
   1303         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1304         Local<Integer> localI = code.getParameter(0, TypeId.INT);
   1305         Local<String> result = code.newLocal(TypeId.STRING);
   1306         Label catchIae = new Label();
   1307         Label catchIse = new Label();
   1308         Label catchRe = new Label();
   1309 
   1310         code.addCatchClause(TypeId.get(IllegalArgumentException.class), catchIae);
   1311         code.addCatchClause(TypeId.get(IllegalStateException.class), catchIse);
   1312         code.addCatchClause(TypeId.get(RuntimeException.class), catchRe);
   1313         MethodId<?, ?> thrower = TEST_TYPE.getMethod(TypeId.VOID, "thrower", TypeId.INT);
   1314         code.invokeStatic(thrower, null, localI);
   1315         code.loadConstant(result, "NONE");
   1316         code.returnValue(result);
   1317 
   1318         code.mark(catchIae);
   1319         code.loadConstant(result, "IAE");
   1320         code.returnValue(result);
   1321 
   1322         code.mark(catchIse);
   1323         code.loadConstant(result, "ISE");
   1324         code.returnValue(result);
   1325 
   1326         code.mark(catchRe);
   1327         code.loadConstant(result, "RE");
   1328         code.returnValue(result);
   1329 
   1330         Method method = getMethod();
   1331         assertEquals("NONE", method.invoke(null, 0));
   1332         assertEquals("IAE", method.invoke(null, 1));
   1333         assertEquals("ISE", method.invoke(null, 2));
   1334         assertEquals("RE", method.invoke(null, 3));
   1335         try {
   1336             method.invoke(null, 4);
   1337             fail();
   1338         } catch (InvocationTargetException expected) {
   1339             assertEquals(IOException.class, expected.getCause().getClass());
   1340         }
   1341     }
   1342 
   1343     @SuppressWarnings("unused") // called by generated code
   1344     public static void thrower(int a) throws Exception {
   1345         switch (a) {
   1346         case 0:
   1347             return;
   1348         case 1:
   1349             throw new IllegalArgumentException();
   1350         case 2:
   1351             throw new IllegalStateException();
   1352         case 3:
   1353             throw new UnsupportedOperationException();
   1354         case 4:
   1355             throw new IOException();
   1356         default:
   1357             throw new AssertionError();
   1358         }
   1359     }
   1360 
   1361     public void testNestedCatchClauses() throws Exception {
   1362         /*
   1363          * public static String call(int a, int b, int c) {
   1364          *   try {
   1365          *     DexMakerTest.thrower(a);
   1366          *     try {
   1367          *       DexMakerTest.thrower(b);
   1368          *     } catch (IllegalArgumentException) {
   1369          *       return "INNER";
   1370          *     }
   1371          *     DexMakerTest.thrower(c);
   1372          *     return "NONE";
   1373          *   } catch (IllegalArgumentException e) {
   1374          *     return "OUTER";
   1375          *   }
   1376          */
   1377         MethodId<?, String> methodId = GENERATED.getMethod(
   1378                 TypeId.STRING, "call", TypeId.INT, TypeId.INT, TypeId.INT);
   1379         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1380         Local<Integer> localA = code.getParameter(0, TypeId.INT);
   1381         Local<Integer> localB = code.getParameter(1, TypeId.INT);
   1382         Local<Integer> localC = code.getParameter(2, TypeId.INT);
   1383         Local<String> localResult = code.newLocal(TypeId.STRING);
   1384         Label catchInner = new Label();
   1385         Label catchOuter = new Label();
   1386 
   1387         TypeId<IllegalArgumentException> iaeType = TypeId.get(IllegalArgumentException.class);
   1388         code.addCatchClause(iaeType, catchOuter);
   1389 
   1390         MethodId<?, ?> thrower = TEST_TYPE.getMethod(TypeId.VOID, "thrower", TypeId.INT);
   1391         code.invokeStatic(thrower, null, localA);
   1392 
   1393         // for the inner catch clause, we stash the old label and put it back afterwards.
   1394         Label previousLabel = code.removeCatchClause(iaeType);
   1395         code.addCatchClause(iaeType, catchInner);
   1396         code.invokeStatic(thrower, null, localB);
   1397         code.removeCatchClause(iaeType);
   1398         code.addCatchClause(iaeType, previousLabel);
   1399         code.invokeStatic(thrower, null, localC);
   1400         code.loadConstant(localResult, "NONE");
   1401         code.returnValue(localResult);
   1402 
   1403         code.mark(catchInner);
   1404         code.loadConstant(localResult, "INNER");
   1405         code.returnValue(localResult);
   1406 
   1407         code.mark(catchOuter);
   1408         code.loadConstant(localResult, "OUTER");
   1409         code.returnValue(localResult);
   1410 
   1411         Method method = getMethod();
   1412         assertEquals("OUTER", method.invoke(null, 1, 0, 0));
   1413         assertEquals("INNER", method.invoke(null, 0, 1, 0));
   1414         assertEquals("OUTER", method.invoke(null, 0, 0, 1));
   1415         assertEquals("NONE", method.invoke(null, 0, 0, 0));
   1416     }
   1417 
   1418     public void testThrow() throws Exception {
   1419         /*
   1420          * public static void call() {
   1421          *   throw new IllegalStateException();
   1422          * }
   1423          */
   1424         MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
   1425         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1426         TypeId<IllegalStateException> iseType = TypeId.get(IllegalStateException.class);
   1427         MethodId<IllegalStateException, Void> iseConstructor = iseType.getConstructor();
   1428         Local<IllegalStateException> localIse = code.newLocal(iseType);
   1429         code.newInstance(localIse, iseConstructor);
   1430         code.throwValue(localIse);
   1431 
   1432         try {
   1433             getMethod().invoke(null);
   1434             fail();
   1435         } catch (InvocationTargetException expected) {
   1436             assertEquals(IllegalStateException.class, expected.getCause().getClass());
   1437         }
   1438     }
   1439 
   1440     public void testUnusedParameters() throws Exception {
   1441         /*
   1442          * public static void call(int unused1, long unused2, long unused3) {}
   1443          */
   1444         MethodId<?, Void> methodId = GENERATED.getMethod(
   1445                 TypeId.VOID, "call", TypeId.INT, TypeId.LONG, TypeId.LONG);
   1446         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1447         code.returnVoid();
   1448         getMethod().invoke(null, 1, 2, 3);
   1449     }
   1450 
   1451     public void testFloatingPointCompare() throws Exception {
   1452         Method floatG = floatingPointCompareMethod(TypeId.FLOAT, 1);
   1453         assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY));
   1454         assertEquals(-1, floatG.invoke(null, 1.0f, 2.0f));
   1455         assertEquals(0, floatG.invoke(null, 1.0f, 1.0f));
   1456         assertEquals(1, floatG.invoke(null, 2.0f, 1.0f));
   1457         assertEquals(1, floatG.invoke(null, 1.0f, Float.NaN));
   1458         assertEquals(1, floatG.invoke(null, Float.NaN, 1.0f));
   1459         assertEquals(1, floatG.invoke(null, Float.NaN, Float.NaN));
   1460         assertEquals(1, floatG.invoke(null, Float.NaN, Float.POSITIVE_INFINITY));
   1461 
   1462         Method floatL = floatingPointCompareMethod(TypeId.FLOAT, -1);
   1463         assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY));
   1464         assertEquals(-1, floatL.invoke(null, 1.0f, 2.0f));
   1465         assertEquals(0, floatL.invoke(null, 1.0f, 1.0f));
   1466         assertEquals(1, floatL.invoke(null, 2.0f, 1.0f));
   1467         assertEquals(-1, floatL.invoke(null, 1.0f, Float.NaN));
   1468         assertEquals(-1, floatL.invoke(null, Float.NaN, 1.0f));
   1469         assertEquals(-1, floatL.invoke(null, Float.NaN, Float.NaN));
   1470         assertEquals(-1, floatL.invoke(null, Float.NaN, Float.POSITIVE_INFINITY));
   1471 
   1472         Method doubleG = floatingPointCompareMethod(TypeId.DOUBLE, 1);
   1473         assertEquals(-1, doubleG.invoke(null, 1.0, Double.POSITIVE_INFINITY));
   1474         assertEquals(-1, doubleG.invoke(null, 1.0, 2.0));
   1475         assertEquals(0, doubleG.invoke(null, 1.0, 1.0));
   1476         assertEquals(1, doubleG.invoke(null, 2.0, 1.0));
   1477         assertEquals(1, doubleG.invoke(null, 1.0, Double.NaN));
   1478         assertEquals(1, doubleG.invoke(null, Double.NaN, 1.0));
   1479         assertEquals(1, doubleG.invoke(null, Double.NaN, Double.NaN));
   1480         assertEquals(1, doubleG.invoke(null, Double.NaN, Double.POSITIVE_INFINITY));
   1481 
   1482         Method doubleL = floatingPointCompareMethod(TypeId.DOUBLE, -1);
   1483         assertEquals(-1, doubleL.invoke(null, 1.0, Double.POSITIVE_INFINITY));
   1484         assertEquals(-1, doubleL.invoke(null, 1.0, 2.0));
   1485         assertEquals(0, doubleL.invoke(null, 1.0, 1.0));
   1486         assertEquals(1, doubleL.invoke(null, 2.0, 1.0));
   1487         assertEquals(-1, doubleL.invoke(null, 1.0, Double.NaN));
   1488         assertEquals(-1, doubleL.invoke(null, Double.NaN, 1.0));
   1489         assertEquals(-1, doubleL.invoke(null, Double.NaN, Double.NaN));
   1490         assertEquals(-1, doubleL.invoke(null, Double.NaN, Double.POSITIVE_INFINITY));
   1491     }
   1492 
   1493     private <T extends Number> Method floatingPointCompareMethod(
   1494             TypeId<T> valueType, int nanValue) throws Exception {
   1495         /*
   1496          * public static int call(float a, float b) {
   1497          *     int result = a <=> b;
   1498          *     return result;
   1499          * }
   1500          */
   1501         reset();
   1502         MethodId<?, Integer> methodId = GENERATED.getMethod(
   1503                 TypeId.INT, "call", valueType, valueType);
   1504         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1505         Local<T> localA = code.getParameter(0, valueType);
   1506         Local<T> localB = code.getParameter(1, valueType);
   1507         Local<Integer> localResult = code.newLocal(TypeId.INT);
   1508         code.compareFloatingPoint(localResult, localA, localB, nanValue);
   1509         code.returnValue(localResult);
   1510         return getMethod();
   1511     }
   1512 
   1513     public void testLongCompare() throws Exception {
   1514         /*
   1515          * public static int call(long a, long b) {
   1516          *   int result = a <=> b;
   1517          *   return result;
   1518          * }
   1519          */
   1520         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.LONG, TypeId.LONG);
   1521         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1522         Local<Long> localA = code.getParameter(0, TypeId.LONG);
   1523         Local<Long> localB = code.getParameter(1, TypeId.LONG);
   1524         Local<Integer> localResult = code.newLocal(TypeId.INT);
   1525         code.compareLongs(localResult, localA, localB);
   1526         code.returnValue(localResult);
   1527 
   1528         Method method = getMethod();
   1529         assertEquals(0, method.invoke(null, Long.MIN_VALUE, Long.MIN_VALUE));
   1530         assertEquals(-1, method.invoke(null, Long.MIN_VALUE, 0));
   1531         assertEquals(-1, method.invoke(null, Long.MIN_VALUE, Long.MAX_VALUE));
   1532         assertEquals(1, method.invoke(null, 0, Long.MIN_VALUE));
   1533         assertEquals(0, method.invoke(null, 0, 0));
   1534         assertEquals(-1, method.invoke(null, 0, Long.MAX_VALUE));
   1535         assertEquals(1, method.invoke(null, Long.MAX_VALUE, Long.MIN_VALUE));
   1536         assertEquals(1, method.invoke(null, Long.MAX_VALUE, 0));
   1537         assertEquals(0, method.invoke(null, Long.MAX_VALUE, Long.MAX_VALUE));
   1538     }
   1539 
   1540     public void testArrayLength() throws Exception {
   1541         Method booleanArrayLength = arrayLengthMethod(BOOLEAN_ARRAY);
   1542         assertEquals(0, booleanArrayLength.invoke(null, new Object[] { new boolean[0] }));
   1543         assertEquals(5, booleanArrayLength.invoke(null, new Object[] { new boolean[5] }));
   1544 
   1545         Method intArrayLength = arrayLengthMethod(INT_ARRAY);
   1546         assertEquals(0, intArrayLength.invoke(null, new Object[] { new int[0] }));
   1547         assertEquals(5, intArrayLength.invoke(null, new Object[] { new int[5] }));
   1548 
   1549         Method longArrayLength = arrayLengthMethod(LONG_ARRAY);
   1550         assertEquals(0, longArrayLength.invoke(null, new Object[] { new long[0] }));
   1551         assertEquals(5, longArrayLength.invoke(null, new Object[] { new long[5] }));
   1552 
   1553         Method objectArrayLength = arrayLengthMethod(OBJECT_ARRAY);
   1554         assertEquals(0, objectArrayLength.invoke(null, new Object[] { new Object[0] }));
   1555         assertEquals(5, objectArrayLength.invoke(null, new Object[] { new Object[5] }));
   1556 
   1557         Method long2dArrayLength = arrayLengthMethod(LONG_2D_ARRAY);
   1558         assertEquals(0, long2dArrayLength.invoke(null, new Object[] { new long[0][0] }));
   1559         assertEquals(5, long2dArrayLength.invoke(null, new Object[] { new long[5][10] }));
   1560     }
   1561 
   1562     private <T> Method arrayLengthMethod(TypeId<T> valueType) throws Exception {
   1563         /*
   1564          * public static int call(long[] array) {
   1565          *   int result = array.length;
   1566          *   return result;
   1567          * }
   1568          */
   1569         reset();
   1570         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", valueType);
   1571         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1572         Local<T> localArray = code.getParameter(0, valueType);
   1573         Local<Integer> localResult = code.newLocal(TypeId.INT);
   1574         code.arrayLength(localResult, localArray);
   1575         code.returnValue(localResult);
   1576         return getMethod();
   1577     }
   1578 
   1579     public void testNewArray() throws Exception {
   1580         Method newBooleanArray = newArrayMethod(BOOLEAN_ARRAY);
   1581         assertEquals("[]", Arrays.toString((boolean[]) newBooleanArray.invoke(null, 0)));
   1582         assertEquals("[false, false, false]",
   1583                 Arrays.toString((boolean[]) newBooleanArray.invoke(null, 3)));
   1584 
   1585         Method newIntArray = newArrayMethod(INT_ARRAY);
   1586         assertEquals("[]", Arrays.toString((int[]) newIntArray.invoke(null, 0)));
   1587         assertEquals("[0, 0, 0]", Arrays.toString((int[]) newIntArray.invoke(null, 3)));
   1588 
   1589         Method newLongArray = newArrayMethod(LONG_ARRAY);
   1590         assertEquals("[]", Arrays.toString((long[]) newLongArray.invoke(null, 0)));
   1591         assertEquals("[0, 0, 0]", Arrays.toString((long[]) newLongArray.invoke(null, 3)));
   1592 
   1593         Method newObjectArray = newArrayMethod(OBJECT_ARRAY);
   1594         assertEquals("[]", Arrays.toString((Object[]) newObjectArray.invoke(null, 0)));
   1595         assertEquals("[null, null, null]",
   1596                 Arrays.toString((Object[]) newObjectArray.invoke(null, 3)));
   1597 
   1598         Method new2dLongArray = newArrayMethod(LONG_2D_ARRAY);
   1599         assertEquals("[]", Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 0)));
   1600         assertEquals("[null, null, null]",
   1601                 Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 3)));
   1602     }
   1603 
   1604     private <T> Method newArrayMethod(TypeId<T> valueType) throws Exception {
   1605         /*
   1606          * public static long[] call(int length) {
   1607          *   long[] result = new long[length];
   1608          *   return result;
   1609          * }
   1610          */
   1611         reset();
   1612         MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", TypeId.INT);
   1613         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1614         Local<Integer> localLength = code.getParameter(0, TypeId.INT);
   1615         Local<T> localResult = code.newLocal(valueType);
   1616         code.newArray(localResult, localLength);
   1617         code.returnValue(localResult);
   1618         return getMethod();
   1619     }
   1620 
   1621     public void testReadAndWriteArray() throws Exception {
   1622         Method swapBooleanArray = arraySwapMethod(BOOLEAN_ARRAY, TypeId.BOOLEAN);
   1623         boolean[] booleans = new boolean[3];
   1624         assertEquals(false, swapBooleanArray.invoke(null, booleans, 1, true));
   1625         assertEquals("[false, true, false]", Arrays.toString(booleans));
   1626 
   1627         Method swapIntArray = arraySwapMethod(INT_ARRAY, TypeId.INT);
   1628         int[] ints = new int[3];
   1629         assertEquals(0, swapIntArray.invoke(null, ints, 1, 5));
   1630         assertEquals("[0, 5, 0]", Arrays.toString(ints));
   1631 
   1632         Method swapLongArray = arraySwapMethod(LONG_ARRAY, TypeId.LONG);
   1633         long[] longs = new long[3];
   1634         assertEquals(0L, swapLongArray.invoke(null, longs, 1, 6L));
   1635         assertEquals("[0, 6, 0]", Arrays.toString(longs));
   1636 
   1637         Method swapObjectArray = arraySwapMethod(OBJECT_ARRAY, TypeId.OBJECT);
   1638         Object[] objects = new Object[3];
   1639         assertEquals(null, swapObjectArray.invoke(null, objects, 1, "X"));
   1640         assertEquals("[null, X, null]", Arrays.toString(objects));
   1641 
   1642         Method swapLong2dArray = arraySwapMethod(LONG_2D_ARRAY, LONG_ARRAY);
   1643         long[][] longs2d = new long[3][];
   1644         assertEquals(null, swapLong2dArray.invoke(null, longs2d, 1, new long[] { 7 }));
   1645         assertEquals("[null, [7], null]", Arrays.deepToString(longs2d));
   1646     }
   1647 
   1648     private <A, T> Method arraySwapMethod(TypeId<A> arrayType, TypeId<T> singleType)
   1649             throws Exception {
   1650         /*
   1651          * public static long swap(long[] array, int index, long newValue) {
   1652          *   long result = array[index];
   1653          *   array[index] = newValue;
   1654          *   return result;
   1655          * }
   1656          */
   1657         reset();
   1658         MethodId<?, T> methodId = GENERATED.getMethod(
   1659                 singleType, "call", arrayType, TypeId.INT, singleType);
   1660         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1661         Local<A> localArray = code.getParameter(0, arrayType);
   1662         Local<Integer> localIndex = code.getParameter(1, TypeId.INT);
   1663         Local<T> localNewValue = code.getParameter(2, singleType);
   1664         Local<T> localResult = code.newLocal(singleType);
   1665         code.aget(localResult, localArray, localIndex);
   1666         code.aput(localArray, localIndex, localNewValue);
   1667         code.returnValue(localResult);
   1668         return getMethod();
   1669     }
   1670 
   1671     public void testSynchronizedFlagImpactsDeclarationOnly() throws Exception {
   1672         /*
   1673          * public synchronized void call() {
   1674          *   wait(100L);
   1675          * }
   1676          */
   1677         MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
   1678         MethodId<Object, Void> wait = TypeId.OBJECT.getMethod(TypeId.VOID, "wait", TypeId.LONG);
   1679         Code code = dexMaker.declare(methodId, PUBLIC | SYNCHRONIZED);
   1680         Local<?> thisLocal = code.getThis(GENERATED);
   1681         Local<Long> timeout = code.newLocal(TypeId.LONG);
   1682         code.loadConstant(timeout, 100L);
   1683         code.invokeVirtual(wait, null, thisLocal, timeout);
   1684         code.returnVoid();
   1685 
   1686         addDefaultConstructor();
   1687 
   1688         Class<?> generatedClass = generateAndLoad();
   1689         Object instance = generatedClass.newInstance();
   1690         Method method = generatedClass.getMethod("call");
   1691         assertTrue(Modifier.isSynchronized(method.getModifiers()));
   1692         try {
   1693             method.invoke(instance);
   1694             fail();
   1695         } catch (InvocationTargetException expected) {
   1696             assertTrue(expected.getCause() instanceof IllegalMonitorStateException);
   1697         }
   1698     }
   1699 
   1700     public void testMonitorEnterMonitorExit() throws Exception {
   1701         /*
   1702          * public synchronized void call() {
   1703          *   synchronized (this) {
   1704          *     wait(100L);
   1705          *   }
   1706          * }
   1707          */
   1708         MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
   1709         MethodId<Object, Void> wait = TypeId.OBJECT.getMethod(TypeId.VOID, "wait", TypeId.LONG);
   1710         Code code = dexMaker.declare(methodId, PUBLIC);
   1711         Local<?> thisLocal = code.getThis(GENERATED);
   1712         Local<Long> timeout = code.newLocal(TypeId.LONG);
   1713         code.monitorEnter(thisLocal);
   1714         code.loadConstant(timeout, 100L);
   1715         code.invokeVirtual(wait, null, thisLocal, timeout);
   1716         code.monitorExit(thisLocal);
   1717         code.returnVoid();
   1718 
   1719         addDefaultConstructor();
   1720 
   1721         Class<?> generatedClass = generateAndLoad();
   1722         Object instance = generatedClass.newInstance();
   1723         Method method = generatedClass.getMethod("call");
   1724         assertFalse(Modifier.isSynchronized(method.getModifiers()));
   1725         method.invoke(instance); // will take 100ms
   1726     }
   1727 
   1728     public void testMoveInt() throws Exception {
   1729         /*
   1730          * public static int call(int a) {
   1731          *   int b = a;
   1732          *   int c = a + b;
   1733          *   return c;
   1734          * }
   1735          */
   1736         MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT);
   1737         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1738         Local<Integer> a = code.getParameter(0, TypeId.INT);
   1739         Local<Integer> b = code.newLocal(TypeId.INT);
   1740         Local<Integer> c = code.newLocal(TypeId.INT);
   1741         code.move(b, a);
   1742         code.op(BinaryOp.ADD, c, a, b);
   1743         code.returnValue(c);
   1744 
   1745         assertEquals(6, getMethod().invoke(null, 3));
   1746     }
   1747 
   1748     public void testPrivateClassesAreUnsupported() {
   1749         try {
   1750             dexMaker.declare(TypeId.get("LPrivateClass;"), "PrivateClass.generated", PRIVATE,
   1751                     TypeId.OBJECT);
   1752             fail();
   1753         } catch (IllegalArgumentException expected) {
   1754         }
   1755     }
   1756 
   1757     public void testAbstractMethodsAreUnsupported() {
   1758         MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
   1759         try {
   1760             dexMaker.declare(methodId, ABSTRACT);
   1761             fail();
   1762         } catch (IllegalArgumentException expected) {
   1763         }
   1764     }
   1765 
   1766     public void testNativeMethodsAreUnsupported() {
   1767         MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
   1768         try {
   1769             dexMaker.declare(methodId, NATIVE);
   1770             fail();
   1771         } catch (IllegalArgumentException expected) {
   1772         }
   1773     }
   1774 
   1775     public void testSynchronizedFieldsAreUnsupported() {
   1776         try {
   1777             FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "synchronizedField");
   1778             dexMaker.declare(fieldId, SYNCHRONIZED, null);
   1779             fail();
   1780         } catch (IllegalArgumentException expected) {
   1781         }
   1782     }
   1783 
   1784     public void testInitialValueWithNonStaticField() {
   1785         try {
   1786             FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "nonStaticField");
   1787             dexMaker.declare(fieldId, 0, 1);
   1788             fail();
   1789         } catch (IllegalArgumentException expected) {
   1790         }
   1791     }
   1792 
   1793     // TODO: cast primitive to non-primitive
   1794     // TODO: cast non-primitive to primitive
   1795     // TODO: cast byte to integer
   1796     // TODO: cast byte to long
   1797     // TODO: cast long to byte
   1798     // TODO: fail if a label is unreachable (never navigated to)
   1799     // TODO: more strict type parameters: Integer on methods
   1800     // TODO: don't generate multiple times (?)
   1801     // TODO: test array types
   1802     // TODO: test generating an interface
   1803     // TODO: declare native method or abstract method
   1804     // TODO: get a thrown exception 'e' into a local
   1805     // TODO: move a primitive or reference
   1806 
   1807     private void addDefaultConstructor() {
   1808         Code code = dexMaker.declare(GENERATED.getConstructor(), PUBLIC);
   1809         Local<?> thisRef = code.getThis(GENERATED);
   1810         code.invokeDirect(TypeId.OBJECT.getConstructor(), null, thisRef);
   1811         code.returnVoid();
   1812     }
   1813 
   1814     public void testCaching_Methods() throws Exception {
   1815         int origSize = getDataDirectory().listFiles().length;
   1816         final String defaultMethodName = "call";
   1817 
   1818         dexMaker = new DexMaker();
   1819         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1820         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1821         generateAndLoad();
   1822         // DexMaker writes two files to disk at a time: Generated_XXXX.jar and Generated_XXXX.dex.
   1823         assertEquals(origSize + 2, getDataDirectory().listFiles().length);
   1824 
   1825         long lastModified  = getJarFiles()[0].lastModified();
   1826 
   1827         // Create new dexmaker generator with same method signature.
   1828         dexMaker = new DexMaker();
   1829         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1830         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1831         generateAndLoad();
   1832         assertEquals(origSize + 2, getDataDirectory().listFiles().length);
   1833         assertEquals(lastModified, getJarFiles()[0].lastModified());
   1834 
   1835         // Create new dexmaker generators with different params.
   1836         dexMaker = new DexMaker();
   1837         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1838         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.DOUBLE);
   1839         generateAndLoad();
   1840         assertEquals(origSize + 4, getDataDirectory().listFiles().length);
   1841 
   1842         dexMaker = new DexMaker();
   1843         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1844         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.DOUBLE);
   1845         generateAndLoad();
   1846         assertEquals(origSize + 6, getDataDirectory().listFiles().length);
   1847 
   1848         // Create new dexmaker generator with different return types.
   1849         dexMaker = new DexMaker();
   1850         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1851         addMethodToDexMakerGenerator(TypeId.DOUBLE, defaultMethodName, TypeId.INT);
   1852         generateAndLoad();
   1853         assertEquals(origSize + 8, getDataDirectory().listFiles().length);
   1854 
   1855         // Create new dexmaker generators with multiple methods.
   1856         dexMaker = new DexMaker();
   1857         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1858         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1859         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.BOOLEAN); // new method
   1860         generateAndLoad();
   1861         assertEquals(origSize + 10, getDataDirectory().listFiles().length);
   1862 
   1863         dexMaker = new DexMaker();
   1864         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1865         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.BOOLEAN);
   1866         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1867         generateAndLoad();
   1868         assertEquals(origSize + 10, getDataDirectory().listFiles().length); // should already be cached.
   1869 
   1870         dexMaker = new DexMaker();
   1871         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1872         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1873         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.INT, TypeId.BOOLEAN); // new method
   1874         generateAndLoad();
   1875         assertEquals(origSize + 12, getDataDirectory().listFiles().length);
   1876 
   1877         dexMaker = new DexMaker();
   1878         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1879         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1880         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.INT); // new method
   1881         generateAndLoad();
   1882         assertEquals(origSize + 14, getDataDirectory().listFiles().length);
   1883 
   1884         dexMaker = new DexMaker();
   1885         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1886         addMethodToDexMakerGenerator(TypeId.INT, "differentName", TypeId.INT); // new method
   1887         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT, TypeId.BOOLEAN);
   1888         generateAndLoad();
   1889         assertEquals(origSize + 16, getDataDirectory().listFiles().length);
   1890     }
   1891 
   1892     public static class BlankClassA {
   1893 
   1894     }
   1895 
   1896     public static class BlankClassB {
   1897 
   1898     }
   1899 
   1900     public void testCaching_DifferentParentClasses() throws Exception {
   1901         int origSize = getDataDirectory().listFiles().length;
   1902         final String defaultMethodName = "call";
   1903 
   1904         // Create new dexmaker generator with BlankClassA as supertype.
   1905         dexMaker = new DexMaker();
   1906         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.get(BlankClassA.class));
   1907         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1908         generateAndLoad();
   1909         // DexMaker writes two files to disk at a time: Generated_XXXX.jar and Generated_XXXX.dex.
   1910         assertEquals(origSize + 2, getDataDirectory().listFiles().length);
   1911 
   1912         // Create new dexmaker generator with BlankClassB as supertype.
   1913         dexMaker = new DexMaker();
   1914         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.get(BlankClassB.class));
   1915         addMethodToDexMakerGenerator(TypeId.INT, defaultMethodName, TypeId.INT);
   1916         generateAndLoad();
   1917         assertEquals(origSize + 4, getDataDirectory().listFiles().length);
   1918 
   1919     }
   1920 
   1921     private void addMethodToDexMakerGenerator(TypeId<?> typeId, String methodName, TypeId<?>... params) throws Exception {
   1922         MethodId<?, ?> methodId = GENERATED.getMethod(typeId, methodName, params);
   1923         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
   1924         TypeId<IllegalStateException> iseType = TypeId.get(IllegalStateException.class);
   1925         Local<IllegalStateException> localIse = code.newLocal(iseType);
   1926         if (params.length > 0) {
   1927             if (params[0] == typeId) {
   1928                 Local<?> localResult = code.getParameter(0, TypeId.INT);
   1929                 code.returnValue(localResult);
   1930             } else {
   1931                 code.throwValue(localIse);
   1932             }
   1933         } else {
   1934             code.throwValue(localIse);
   1935         }
   1936     }
   1937 
   1938     public void testCaching_Constructors() throws Exception {
   1939         int origSize = getDataDirectory().listFiles().length;
   1940 
   1941         // Create new dexmaker generator with Generated(int) constructor.
   1942         dexMaker = new DexMaker();
   1943         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1944         addConstructorToDexMakerGenerator(TypeId.INT);
   1945         generateAndLoad();
   1946         // DexMaker writes two files to disk at a time: Generated_XXXX.jar and Generated_XXXX.dex.
   1947         assertEquals(origSize + 2, getDataDirectory().listFiles().length);
   1948 
   1949         long lastModified  = getJarFiles()[0].lastModified();
   1950 
   1951         dexMaker = new DexMaker();
   1952         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1953         addConstructorToDexMakerGenerator(TypeId.INT);
   1954         generateAndLoad();
   1955         assertEquals(origSize + 2, getDataDirectory().listFiles().length);
   1956         assertEquals(lastModified, getJarFiles()[0].lastModified());
   1957 
   1958         // Create new dexmaker generator with Generated(boolean) constructor.
   1959         dexMaker = new DexMaker();
   1960         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1961         addConstructorToDexMakerGenerator(TypeId.BOOLEAN);
   1962         generateAndLoad();
   1963         assertEquals(origSize + 4, getDataDirectory().listFiles().length);
   1964 
   1965         // Create new dexmaker generator with multiple constructors.
   1966         dexMaker = new DexMaker();
   1967         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1968         addConstructorToDexMakerGenerator(TypeId.INT);
   1969         addConstructorToDexMakerGenerator(TypeId.BOOLEAN);
   1970         generateAndLoad();
   1971         assertEquals(origSize + 6, getDataDirectory().listFiles().length);
   1972 
   1973         // Ensure that order of constructors does not affect caching decision.
   1974         dexMaker = new DexMaker();
   1975         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
   1976         addConstructorToDexMakerGenerator(TypeId.BOOLEAN);
   1977         addConstructorToDexMakerGenerator(TypeId.INT);
   1978         generateAndLoad();
   1979         assertEquals(origSize + 6, getDataDirectory().listFiles().length);
   1980     }
   1981 
   1982     private void addConstructorToDexMakerGenerator(TypeId<?>... params) throws Exception {
   1983         MethodId<?, Void> constructor = GENERATED.getConstructor(params);
   1984         Code code = dexMaker.declare(constructor, PUBLIC);
   1985         code.returnVoid();
   1986     }
   1987 
   1988     private File[] getJarFiles() {
   1989         return getDataDirectory().listFiles(new FilenameFilter() {
   1990             public boolean accept(File dir, String name) {
   1991                 return name.endsWith(".jar");
   1992             }
   1993         });
   1994     }
   1995 
   1996     /**
   1997      * Returns the generated method.
   1998      */
   1999     private Method getMethod() throws Exception {
   2000         Class<?> generated = generateAndLoad();
   2001         for (Method method : generated.getMethods()) {
   2002             if (method.getName().equals("call")) {
   2003                 return method;
   2004             }
   2005         }
   2006         throw new IllegalStateException("no call() method");
   2007     }
   2008 
   2009     public static File getDataDirectory() {
   2010         String envVariable = "ANDROID_DATA";
   2011         String defaultLoc = "/data";
   2012         String path = System.getenv(envVariable);
   2013         return path == null ? new File(defaultLoc) : new File(path);
   2014     }
   2015 
   2016     private Class<?> generateAndLoad() throws Exception {
   2017         return dexMaker.generateAndLoad(getClass().getClassLoader(), getDataDirectory())
   2018                 .loadClass("Generated");
   2019     }
   2020 }
   2021