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