• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
setUp()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      */
reset()66     private void reset() {
67         dexMaker = new DexMaker();
68         dexMaker.declare(GENERATED, "Generated.java", PUBLIC, TypeId.OBJECT);
69     }
70 
testNewInstance()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;
Constructable(long a, boolean b)98         public Constructable(long a, boolean b) {
99             this.a = a;
100             this.b = b;
101         }
102     }
103 
testVoidNoArgMemberMethod()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 
testInvokeStatic()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 
testCreateLocalMethodAsNull()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
staticMethod(int a)162     public static int staticMethod(int a) {
163         return a + 6;
164     }
165 
testInvokeVirtual()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
virtualMethod(int a)187     public int virtualMethod(int a) {
188         return a + 5;
189     }
190 
testInvokeDirect()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 
testInvokeSuper()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 
testInvokeInterface()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 
testInvokeVoidMethodIgnoresTargetLocal()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
voidMethod()301     public static void voidMethod() {
302     }
303 
testParameterMismatch()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 
testInvokeTypeSafety()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 
testReturnTypeMismatch()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 
testDeclareStaticFields()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 
testDeclareInstanceFields()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      */
testDeclareConstructor()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 
testReturnType()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 
testReturnType(Class<T> javaType, T value)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 
testBranching()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 
branchingMethod(Comparison comparison)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 
testCastIntegerToInteger()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 
testCastIntegerToFloatingPoint()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 
testCastFloatingPointToInteger()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 
testCastFloatingPointToFloatingPoint()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 
numericCastingMethod(Class<?> source, Class<?> target)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 
testNot()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 
notMethod(Class<T> source)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 
testNegate()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 
negateMethod(Class<T> source)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 
testIntBinaryOps()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 
testLongBinaryOps()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 
testFloatBinaryOps()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 
testDoubleBinaryOps()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 
binaryOpMethod( Class<T1> valueAClass, Class<T2> valueBClass, BinaryOp op)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 
testReadAndWriteInstanceFields()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 
instanceSwapMethod( Class<V> valueClass, String fieldName)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 
testReadAndWriteStaticFields()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 
staticSwapMethod(Class<V> valueClass, String fieldName)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 
testTypeCast()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 
testInstanceOf()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      */
testForLoop()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      */
testWhileLoop()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 
testIfElseBlock()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 
testRecursion()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 
testCatchExceptions()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
thrower(int a)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 
testNestedCatchClauses()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 
testThrow()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 
testUnusedParameters()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 
testFloatingPointCompare()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 
floatingPointCompareMethod( TypeId<T> valueType, int nanValue)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 
testLongCompare()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 
testArrayLength()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 
arrayLengthMethod(TypeId<T> valueType)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 
testNewArray()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 
newArrayMethod(TypeId<T> valueType)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 
testReadAndWriteArray()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 
arraySwapMethod(TypeId<A> arrayType, TypeId<T> singleType)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 
testSynchronizedFlagImpactsDeclarationOnly()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 
testMonitorEnterMonitorExit()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 
testMoveInt()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 
testPrivateClassesAreUnsupported()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 
testAbstractMethodsAreUnsupported()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 
testNativeMethodsAreUnsupported()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 
testSynchronizedFieldsAreUnsupported()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 
testInitialValueWithNonStaticField()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 
addDefaultConstructor()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      */
getMethod()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 
getDataDirectory()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 
generateAndLoad()1824     private Class<?> generateAndLoad() throws Exception {
1825         return dexMaker.generateAndLoad(getClass().getClassLoader(), getDataDirectory())
1826                 .loadClass("Generated");
1827     }
1828 }
1829