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