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