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