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