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