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