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, int.class, BinaryOp.ADD); 734 assertEquals(79, add.invoke(null, 75, 4)); 735 736 Method subtract = binaryOpMethod(int.class, int.class, BinaryOp.SUBTRACT); 737 assertEquals(71, subtract.invoke(null, 75, 4)); 738 739 Method multiply = binaryOpMethod(int.class, int.class, BinaryOp.MULTIPLY); 740 assertEquals(300, multiply.invoke(null, 75, 4)); 741 742 Method divide = binaryOpMethod(int.class, int.class, BinaryOp.DIVIDE); 743 assertEquals(18, divide.invoke(null, 75, 4)); 744 try { 745 divide.invoke(null, 75, 0); 746 fail(); 747 } catch (InvocationTargetException expected) { 748 assertEquals(ArithmeticException.class, expected.getCause().getClass()); 749 } 750 751 Method remainder = binaryOpMethod(int.class, int.class, BinaryOp.REMAINDER); 752 assertEquals(3, remainder.invoke(null, 75, 4)); 753 try { 754 remainder.invoke(null, 75, 0); 755 fail(); 756 } catch (InvocationTargetException expected) { 757 assertEquals(ArithmeticException.class, expected.getCause().getClass()); 758 } 759 760 Method and = binaryOpMethod(int.class, int.class, BinaryOp.AND); 761 assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000)); 762 763 Method or = binaryOpMethod(int.class, int.class, BinaryOp.OR); 764 assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000)); 765 766 Method xor = binaryOpMethod(int.class, int.class, BinaryOp.XOR); 767 assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000)); 768 769 Method shiftLeft = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_LEFT); 770 assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8)); 771 772 Method shiftRight = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_RIGHT); 773 assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8)); 774 775 Method unsignedShiftRight = binaryOpMethod(int.class, 776 int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT); 777 assertEquals(0x00abcd12, unsignedShiftRight.invoke(null, 0xabcd1234, 8)); 778 } 779 780 public void testLongBinaryOps() throws Exception { 781 Method add = binaryOpMethod(long.class, long.class, BinaryOp.ADD); 782 assertEquals(30000000079L, add.invoke(null, 10000000075L, 20000000004L)); 783 784 Method subtract = binaryOpMethod(long.class, long.class, BinaryOp.SUBTRACT); 785 assertEquals(20000000071L, subtract.invoke(null, 30000000075L, 10000000004L)); 786 787 Method multiply = binaryOpMethod(long.class, long.class, BinaryOp.MULTIPLY); 788 assertEquals(-8742552812415203028L, multiply.invoke(null, 30000000075L, 20000000004L)); 789 790 Method divide = binaryOpMethod(long.class, long.class, BinaryOp.DIVIDE); 791 assertEquals(-2L, divide.invoke(null, -8742552812415203028L, 4142552812415203028L)); 792 try { 793 divide.invoke(null, -8742552812415203028L, 0L); 794 fail(); 795 } catch (InvocationTargetException expected) { 796 assertEquals(ArithmeticException.class, expected.getCause().getClass()); 797 } 798 799 Method remainder = binaryOpMethod(long.class, long.class, BinaryOp.REMAINDER); 800 assertEquals(10000000004L, remainder.invoke(null, 30000000079L, 20000000075L)); 801 try { 802 remainder.invoke(null, 30000000079L, 0L); 803 fail(); 804 } catch (InvocationTargetException expected) { 805 assertEquals(ArithmeticException.class, expected.getCause().getClass()); 806 } 807 808 Method and = binaryOpMethod(long.class, long.class, BinaryOp.AND); 809 assertEquals(0xff00ff0000000000L, 810 and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); 811 812 Method or = binaryOpMethod(long.class, long.class, BinaryOp.OR); 813 assertEquals(0xffffffffff00ff00L, 814 or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); 815 816 Method xor = binaryOpMethod(long.class, long.class, BinaryOp.XOR); 817 assertEquals(0x00ff00ffff00ff00L, 818 xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); 819 820 Method shiftLeft = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_LEFT); 821 assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8)); 822 823 Method shiftRight = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_RIGHT); 824 assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8)); 825 826 Method unsignedShiftRight = binaryOpMethod( 827 long.class, int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT); 828 assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8)); 829 } 830 831 public void testFloatBinaryOps() throws Exception { 832 Method add = binaryOpMethod(float.class, float.class, BinaryOp.ADD); 833 assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f)); 834 835 Method subtract = binaryOpMethod(float.class, float.class, BinaryOp.SUBTRACT); 836 assertEquals(4.25f, subtract.invoke(null, 5.5f, 1.25f)); 837 838 Method multiply = binaryOpMethod(float.class, float.class, BinaryOp.MULTIPLY); 839 assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f)); 840 841 Method divide = binaryOpMethod(float.class, float.class, BinaryOp.DIVIDE); 842 assertEquals(4.4f, divide.invoke(null, 5.5f, 1.25f)); 843 assertEquals(Float.POSITIVE_INFINITY, divide.invoke(null, 5.5f, 0.0f)); 844 845 Method remainder = binaryOpMethod(float.class, float.class, BinaryOp.REMAINDER); 846 assertEquals(0.5f, remainder.invoke(null, 5.5f, 1.25f)); 847 assertEquals(Float.NaN, remainder.invoke(null, 5.5f, 0.0f)); 848 } 849 850 public void testDoubleBinaryOps() throws Exception { 851 Method add = binaryOpMethod(double.class, double.class, BinaryOp.ADD); 852 assertEquals(6.75, add.invoke(null, 5.5, 1.25)); 853 854 Method subtract = binaryOpMethod(double.class, double.class, BinaryOp.SUBTRACT); 855 assertEquals(4.25, subtract.invoke(null, 5.5, 1.25)); 856 857 Method multiply = binaryOpMethod(double.class, double.class, BinaryOp.MULTIPLY); 858 assertEquals(6.875, multiply.invoke(null, 5.5, 1.25)); 859 860 Method divide = binaryOpMethod(double.class, double.class, BinaryOp.DIVIDE); 861 assertEquals(4.4, divide.invoke(null, 5.5, 1.25)); 862 assertEquals(Double.POSITIVE_INFINITY, divide.invoke(null, 5.5, 0.0)); 863 864 Method remainder = binaryOpMethod(double.class, double.class, BinaryOp.REMAINDER); 865 assertEquals(0.5, remainder.invoke(null, 5.5, 1.25)); 866 assertEquals(Double.NaN, remainder.invoke(null, 5.5, 0.0)); 867 } 868 869 private <T1, T2> Method binaryOpMethod( 870 Class<T1> valueAClass, Class<T2> valueBClass, BinaryOp op) throws Exception { 871 /* 872 * public static int binaryOp(int a, int b) { 873 * int result = a + b; 874 * return result; 875 * } 876 */ 877 reset(); 878 TypeId<T1> valueAType = TypeId.get(valueAClass); 879 TypeId<T2> valueBType = TypeId.get(valueBClass); 880 MethodId<?, T1> methodId = GENERATED.getMethod(valueAType, "call", valueAType, valueBType); 881 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 882 Local<T1> localA = code.getParameter(0, valueAType); 883 Local<T2> localB = code.getParameter(1, valueBType); 884 Local<T1> localResult = code.newLocal(valueAType); 885 code.op(op, localResult, localA, localB); 886 code.returnValue(localResult); 887 return getMethod(); 888 } 889 890 public void testReadAndWriteInstanceFields() throws Exception { 891 Instance instance = new Instance(); 892 893 Method intSwap = instanceSwapMethod(int.class, "intValue"); 894 instance.intValue = 5; 895 assertEquals(5, intSwap.invoke(null, instance, 10)); 896 assertEquals(10, instance.intValue); 897 898 Method longSwap = instanceSwapMethod(long.class, "longValue"); 899 instance.longValue = 500L; 900 assertEquals(500L, longSwap.invoke(null, instance, 1234L)); 901 assertEquals(1234L, instance.longValue); 902 903 Method booleanSwap = instanceSwapMethod(boolean.class, "booleanValue"); 904 instance.booleanValue = false; 905 assertEquals(false, booleanSwap.invoke(null, instance, true)); 906 assertEquals(true, instance.booleanValue); 907 908 Method floatSwap = instanceSwapMethod(float.class, "floatValue"); 909 instance.floatValue = 1.5f; 910 assertEquals(1.5f, floatSwap.invoke(null, instance, 0.5f)); 911 assertEquals(0.5f, instance.floatValue); 912 913 Method doubleSwap = instanceSwapMethod(double.class, "doubleValue"); 914 instance.doubleValue = 155.5; 915 assertEquals(155.5, doubleSwap.invoke(null, instance, 266.6)); 916 assertEquals(266.6, instance.doubleValue); 917 918 Method objectSwap = instanceSwapMethod(Object.class, "objectValue"); 919 instance.objectValue = "before"; 920 assertEquals("before", objectSwap.invoke(null, instance, "after")); 921 assertEquals("after", instance.objectValue); 922 923 Method byteSwap = instanceSwapMethod(byte.class, "byteValue"); 924 instance.byteValue = 0x35; 925 assertEquals((byte) 0x35, byteSwap.invoke(null, instance, (byte) 0x64)); 926 assertEquals((byte) 0x64, instance.byteValue); 927 928 Method charSwap = instanceSwapMethod(char.class, "charValue"); 929 instance.charValue = 'A'; 930 assertEquals('A', charSwap.invoke(null, instance, 'B')); 931 assertEquals('B', instance.charValue); 932 933 Method shortSwap = instanceSwapMethod(short.class, "shortValue"); 934 instance.shortValue = (short) 0xabcd; 935 assertEquals((short) 0xabcd, shortSwap.invoke(null, instance, (short) 0x1234)); 936 assertEquals((short) 0x1234, instance.shortValue); 937 } 938 939 public class Instance { 940 public int intValue; 941 public long longValue; 942 public float floatValue; 943 public double doubleValue; 944 public Object objectValue; 945 public boolean booleanValue; 946 public byte byteValue; 947 public char charValue; 948 public short shortValue; 949 } 950 951 private <V> Method instanceSwapMethod( 952 Class<V> valueClass, String fieldName) throws Exception { 953 /* 954 * public static int call(Instance instance, int newValue) { 955 * int oldValue = instance.intValue; 956 * instance.intValue = newValue; 957 * return oldValue; 958 * } 959 */ 960 reset(); 961 TypeId<V> valueType = TypeId.get(valueClass); 962 TypeId<Instance> objectType = TypeId.get(Instance.class); 963 FieldId<Instance, V> fieldId = objectType.getField(valueType, fieldName); 964 MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", objectType, valueType); 965 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 966 Local<Instance> localInstance = code.getParameter(0, objectType); 967 Local<V> localNewValue = code.getParameter(1, valueType); 968 Local<V> localOldValue = code.newLocal(valueType); 969 code.iget(fieldId, localOldValue, localInstance); 970 code.iput(fieldId, localInstance, localNewValue); 971 code.returnValue(localOldValue); 972 return getMethod(); 973 } 974 975 public void testReadAndWriteStaticFields() throws Exception { 976 Method intSwap = staticSwapMethod(int.class, "intValue"); 977 Static.intValue = 5; 978 assertEquals(5, intSwap.invoke(null, 10)); 979 assertEquals(10, Static.intValue); 980 981 Method longSwap = staticSwapMethod(long.class, "longValue"); 982 Static.longValue = 500L; 983 assertEquals(500L, longSwap.invoke(null, 1234L)); 984 assertEquals(1234L, Static.longValue); 985 986 Method booleanSwap = staticSwapMethod(boolean.class, "booleanValue"); 987 Static.booleanValue = false; 988 assertEquals(false, booleanSwap.invoke(null, true)); 989 assertEquals(true, Static.booleanValue); 990 991 Method floatSwap = staticSwapMethod(float.class, "floatValue"); 992 Static.floatValue = 1.5f; 993 assertEquals(1.5f, floatSwap.invoke(null, 0.5f)); 994 assertEquals(0.5f, Static.floatValue); 995 996 Method doubleSwap = staticSwapMethod(double.class, "doubleValue"); 997 Static.doubleValue = 155.5; 998 assertEquals(155.5, doubleSwap.invoke(null, 266.6)); 999 assertEquals(266.6, Static.doubleValue); 1000 1001 Method objectSwap = staticSwapMethod(Object.class, "objectValue"); 1002 Static.objectValue = "before"; 1003 assertEquals("before", objectSwap.invoke(null, "after")); 1004 assertEquals("after", Static.objectValue); 1005 1006 Method byteSwap = staticSwapMethod(byte.class, "byteValue"); 1007 Static.byteValue = 0x35; 1008 assertEquals((byte) 0x35, byteSwap.invoke(null, (byte) 0x64)); 1009 assertEquals((byte) 0x64, Static.byteValue); 1010 1011 Method charSwap = staticSwapMethod(char.class, "charValue"); 1012 Static.charValue = 'A'; 1013 assertEquals('A', charSwap.invoke(null, 'B')); 1014 assertEquals('B', Static.charValue); 1015 1016 Method shortSwap = staticSwapMethod(short.class, "shortValue"); 1017 Static.shortValue = (short) 0xabcd; 1018 assertEquals((short) 0xabcd, shortSwap.invoke(null, (short) 0x1234)); 1019 assertEquals((short) 0x1234, Static.shortValue); 1020 } 1021 1022 public static class Static { 1023 public static int intValue; 1024 public static long longValue; 1025 public static float floatValue; 1026 public static double doubleValue; 1027 public static Object objectValue; 1028 public static boolean booleanValue; 1029 public static byte byteValue; 1030 public static char charValue; 1031 public static short shortValue; 1032 } 1033 1034 private <V> Method staticSwapMethod(Class<V> valueClass, String fieldName) 1035 throws Exception { 1036 /* 1037 * public static int call(int newValue) { 1038 * int oldValue = Static.intValue; 1039 * Static.intValue = newValue; 1040 * return oldValue; 1041 * } 1042 */ 1043 reset(); 1044 TypeId<V> valueType = TypeId.get(valueClass); 1045 TypeId<Static> objectType = TypeId.get(Static.class); 1046 FieldId<Static, V> fieldId = objectType.getField(valueType, fieldName); 1047 MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", valueType); 1048 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1049 Local<V> localNewValue = code.getParameter(0, valueType); 1050 Local<V> localOldValue = code.newLocal(valueType); 1051 code.sget(fieldId, localOldValue); 1052 code.sput(fieldId, localNewValue); 1053 code.returnValue(localOldValue); 1054 return getMethod(); 1055 } 1056 1057 public void testTypeCast() throws Exception { 1058 /* 1059 * public static String call(Object o) { 1060 * String s = (String) o; 1061 * } 1062 */ 1063 MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call", TypeId.OBJECT); 1064 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1065 Local<Object> localObject = code.getParameter(0, TypeId.OBJECT); 1066 Local<String> localString = code.newLocal(TypeId.STRING); 1067 code.cast(localString, localObject); 1068 code.returnValue(localString); 1069 1070 Method method = getMethod(); 1071 assertEquals("s", method.invoke(null, "s")); 1072 assertEquals(null, method.invoke(null, (String) null)); 1073 try { 1074 method.invoke(null, 5); 1075 fail(); 1076 } catch (InvocationTargetException expected) { 1077 assertEquals(ClassCastException.class, expected.getCause().getClass()); 1078 } 1079 } 1080 1081 public void testInstanceOf() throws Exception { 1082 /* 1083 * public static boolean call(Object o) { 1084 * boolean result = o instanceof String; 1085 * return result; 1086 * } 1087 */ 1088 MethodId<?, Boolean> methodId = GENERATED.getMethod(TypeId.BOOLEAN, "call", TypeId.OBJECT); 1089 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1090 Local<Object> localObject = code.getParameter(0, TypeId.OBJECT); 1091 Local<Boolean> localResult = code.newLocal(TypeId.BOOLEAN); 1092 code.instanceOfType(localResult, localObject, TypeId.STRING); 1093 code.returnValue(localResult); 1094 1095 Method method = getMethod(); 1096 assertEquals(true, method.invoke(null, "s")); 1097 assertEquals(false, method.invoke(null, (String) null)); 1098 assertEquals(false, method.invoke(null, 5)); 1099 } 1100 1101 /** 1102 * Tests that we can construct a for loop. 1103 */ 1104 public void testForLoop() throws Exception { 1105 /* 1106 * public static int call(int count) { 1107 * int result = 1; 1108 * for (int i = 0; i < count; i += 1) { 1109 * result = result * 2; 1110 * } 1111 * return result; 1112 * } 1113 */ 1114 MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT); 1115 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1116 Local<Integer> localCount = code.getParameter(0, TypeId.INT); 1117 Local<Integer> localResult = code.newLocal(TypeId.INT); 1118 Local<Integer> localI = code.newLocal(TypeId.INT); 1119 Local<Integer> local1 = code.newLocal(TypeId.INT); 1120 Local<Integer> local2 = code.newLocal(TypeId.INT); 1121 code.loadConstant(local1, 1); 1122 code.loadConstant(local2, 2); 1123 code.loadConstant(localResult, 1); 1124 code.loadConstant(localI, 0); 1125 Label loopCondition = new Label(); 1126 Label loopBody = new Label(); 1127 Label afterLoop = new Label(); 1128 code.mark(loopCondition); 1129 code.compare(Comparison.LT, loopBody, localI, localCount); 1130 code.jump(afterLoop); 1131 code.mark(loopBody); 1132 code.op(BinaryOp.MULTIPLY, localResult, localResult, local2); 1133 code.op(BinaryOp.ADD, localI, localI, local1); 1134 code.jump(loopCondition); 1135 code.mark(afterLoop); 1136 code.returnValue(localResult); 1137 1138 Method pow2 = getMethod(); 1139 assertEquals(1, pow2.invoke(null, 0)); 1140 assertEquals(2, pow2.invoke(null, 1)); 1141 assertEquals(4, pow2.invoke(null, 2)); 1142 assertEquals(8, pow2.invoke(null, 3)); 1143 assertEquals(16, pow2.invoke(null, 4)); 1144 } 1145 1146 /** 1147 * Tests that we can construct a while loop. 1148 */ 1149 public void testWhileLoop() throws Exception { 1150 /* 1151 * public static int call(int max) { 1152 * int result = 1; 1153 * while (result < max) { 1154 * result = result * 2; 1155 * } 1156 * return result; 1157 * } 1158 */ 1159 MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT); 1160 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1161 Local<Integer> localMax = code.getParameter(0, TypeId.INT); 1162 Local<Integer> localResult = code.newLocal(TypeId.INT); 1163 Local<Integer> local2 = code.newLocal(TypeId.INT); 1164 code.loadConstant(localResult, 1); 1165 code.loadConstant(local2, 2); 1166 Label loopCondition = new Label(); 1167 Label loopBody = new Label(); 1168 Label afterLoop = new Label(); 1169 code.mark(loopCondition); 1170 code.compare(Comparison.LT, loopBody, localResult, localMax); 1171 code.jump(afterLoop); 1172 code.mark(loopBody); 1173 code.op(BinaryOp.MULTIPLY, localResult, localResult, local2); 1174 code.jump(loopCondition); 1175 code.mark(afterLoop); 1176 code.returnValue(localResult); 1177 1178 Method ceilPow2 = getMethod(); 1179 assertEquals(1, ceilPow2.invoke(null, 1)); 1180 assertEquals(2, ceilPow2.invoke(null, 2)); 1181 assertEquals(4, ceilPow2.invoke(null, 3)); 1182 assertEquals(16, ceilPow2.invoke(null, 10)); 1183 assertEquals(128, ceilPow2.invoke(null, 100)); 1184 assertEquals(1024, ceilPow2.invoke(null, 1000)); 1185 } 1186 1187 public void testIfElseBlock() throws Exception { 1188 /* 1189 * public static int call(int a, int b, int c) { 1190 * if (a < b) { 1191 * if (a < c) { 1192 * return a; 1193 * } else { 1194 * return c; 1195 * } 1196 * } else if (b < c) { 1197 * return b; 1198 * } else { 1199 * return c; 1200 * } 1201 * } 1202 */ 1203 MethodId<?, Integer> methodId = GENERATED.getMethod( 1204 TypeId.INT, "call", TypeId.INT, TypeId.INT, TypeId.INT); 1205 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1206 Local<Integer> localA = code.getParameter(0, TypeId.INT); 1207 Local<Integer> localB = code.getParameter(1, TypeId.INT); 1208 Local<Integer> localC = code.getParameter(2, TypeId.INT); 1209 Label aLessThanB = new Label(); 1210 Label aLessThanC = new Label(); 1211 Label bLessThanC = new Label(); 1212 code.compare(Comparison.LT, aLessThanB, localA, localB); 1213 code.compare(Comparison.LT, bLessThanC, localB, localC); 1214 code.returnValue(localC); 1215 // (a < b) 1216 code.mark(aLessThanB); 1217 code.compare(Comparison.LT, aLessThanC, localA, localC); 1218 code.returnValue(localC); 1219 // (a < c) 1220 code.mark(aLessThanC); 1221 code.returnValue(localA); 1222 // (b < c) 1223 code.mark(bLessThanC); 1224 code.returnValue(localB); 1225 1226 Method min = getMethod(); 1227 assertEquals(1, min.invoke(null, 1, 2, 3)); 1228 assertEquals(1, min.invoke(null, 2, 3, 1)); 1229 assertEquals(1, min.invoke(null, 2, 1, 3)); 1230 assertEquals(1, min.invoke(null, 3, 2, 1)); 1231 } 1232 1233 public void testRecursion() throws Exception { 1234 /* 1235 * public static int call(int a) { 1236 * if (a < 2) { 1237 * return a; 1238 * } 1239 * a -= 1; 1240 * int x = call(a) 1241 * a -= 1; 1242 * int y = call(a); 1243 * int result = x + y; 1244 * return result; 1245 * } 1246 */ 1247 MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT); 1248 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1249 Local<Integer> localA = code.getParameter(0, TypeId.INT); 1250 Local<Integer> local1 = code.newLocal(TypeId.INT); 1251 Local<Integer> local2 = code.newLocal(TypeId.INT); 1252 Local<Integer> localX = code.newLocal(TypeId.INT); 1253 Local<Integer> localY = code.newLocal(TypeId.INT); 1254 Local<Integer> localResult = code.newLocal(TypeId.INT); 1255 Label baseCase = new Label(); 1256 code.loadConstant(local1, 1); 1257 code.loadConstant(local2, 2); 1258 code.compare(Comparison.LT, baseCase, localA, local2); 1259 code.op(BinaryOp.SUBTRACT, localA, localA, local1); 1260 code.invokeStatic(methodId, localX, localA); 1261 code.op(BinaryOp.SUBTRACT, localA, localA, local1); 1262 code.invokeStatic(methodId, localY, localA); 1263 code.op(BinaryOp.ADD, localResult, localX, localY); 1264 code.returnValue(localResult); 1265 code.mark(baseCase); 1266 code.returnValue(localA); 1267 1268 Method fib = getMethod(); 1269 assertEquals(0, fib.invoke(null, 0)); 1270 assertEquals(1, fib.invoke(null, 1)); 1271 assertEquals(1, fib.invoke(null, 2)); 1272 assertEquals(2, fib.invoke(null, 3)); 1273 assertEquals(3, fib.invoke(null, 4)); 1274 assertEquals(5, fib.invoke(null, 5)); 1275 assertEquals(8, fib.invoke(null, 6)); 1276 } 1277 1278 public void testCatchExceptions() throws Exception { 1279 /* 1280 * public static String call(int i) { 1281 * try { 1282 * DexMakerTest.thrower(i); 1283 * return "NONE"; 1284 * } catch (IllegalArgumentException e) { 1285 * return "IAE"; 1286 * } catch (IllegalStateException e) { 1287 * return "ISE"; 1288 * } catch (RuntimeException e) { 1289 * return "RE"; 1290 * } 1291 */ 1292 MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call", TypeId.INT); 1293 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1294 Local<Integer> localI = code.getParameter(0, TypeId.INT); 1295 Local<String> result = code.newLocal(TypeId.STRING); 1296 Label catchIae = new Label(); 1297 Label catchIse = new Label(); 1298 Label catchRe = new Label(); 1299 1300 code.addCatchClause(TypeId.get(IllegalArgumentException.class), catchIae); 1301 code.addCatchClause(TypeId.get(IllegalStateException.class), catchIse); 1302 code.addCatchClause(TypeId.get(RuntimeException.class), catchRe); 1303 MethodId<?, ?> thrower = TEST_TYPE.getMethod(TypeId.VOID, "thrower", TypeId.INT); 1304 code.invokeStatic(thrower, null, localI); 1305 code.loadConstant(result, "NONE"); 1306 code.returnValue(result); 1307 1308 code.mark(catchIae); 1309 code.loadConstant(result, "IAE"); 1310 code.returnValue(result); 1311 1312 code.mark(catchIse); 1313 code.loadConstant(result, "ISE"); 1314 code.returnValue(result); 1315 1316 code.mark(catchRe); 1317 code.loadConstant(result, "RE"); 1318 code.returnValue(result); 1319 1320 Method method = getMethod(); 1321 assertEquals("NONE", method.invoke(null, 0)); 1322 assertEquals("IAE", method.invoke(null, 1)); 1323 assertEquals("ISE", method.invoke(null, 2)); 1324 assertEquals("RE", method.invoke(null, 3)); 1325 try { 1326 method.invoke(null, 4); 1327 fail(); 1328 } catch (InvocationTargetException expected) { 1329 assertEquals(IOException.class, expected.getCause().getClass()); 1330 } 1331 } 1332 1333 @SuppressWarnings("unused") // called by generated code 1334 public static void thrower(int a) throws Exception { 1335 switch (a) { 1336 case 0: 1337 return; 1338 case 1: 1339 throw new IllegalArgumentException(); 1340 case 2: 1341 throw new IllegalStateException(); 1342 case 3: 1343 throw new UnsupportedOperationException(); 1344 case 4: 1345 throw new IOException(); 1346 default: 1347 throw new AssertionError(); 1348 } 1349 } 1350 1351 public void testNestedCatchClauses() throws Exception { 1352 /* 1353 * public static String call(int a, int b, int c) { 1354 * try { 1355 * DexMakerTest.thrower(a); 1356 * try { 1357 * DexMakerTest.thrower(b); 1358 * } catch (IllegalArgumentException) { 1359 * return "INNER"; 1360 * } 1361 * DexMakerTest.thrower(c); 1362 * return "NONE"; 1363 * } catch (IllegalArgumentException e) { 1364 * return "OUTER"; 1365 * } 1366 */ 1367 MethodId<?, String> methodId = GENERATED.getMethod( 1368 TypeId.STRING, "call", TypeId.INT, TypeId.INT, TypeId.INT); 1369 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1370 Local<Integer> localA = code.getParameter(0, TypeId.INT); 1371 Local<Integer> localB = code.getParameter(1, TypeId.INT); 1372 Local<Integer> localC = code.getParameter(2, TypeId.INT); 1373 Local<String> localResult = code.newLocal(TypeId.STRING); 1374 Label catchInner = new Label(); 1375 Label catchOuter = new Label(); 1376 1377 TypeId<IllegalArgumentException> iaeType = TypeId.get(IllegalArgumentException.class); 1378 code.addCatchClause(iaeType, catchOuter); 1379 1380 MethodId<?, ?> thrower = TEST_TYPE.getMethod(TypeId.VOID, "thrower", TypeId.INT); 1381 code.invokeStatic(thrower, null, localA); 1382 1383 // for the inner catch clause, we stash the old label and put it back afterwards. 1384 Label previousLabel = code.removeCatchClause(iaeType); 1385 code.addCatchClause(iaeType, catchInner); 1386 code.invokeStatic(thrower, null, localB); 1387 code.removeCatchClause(iaeType); 1388 code.addCatchClause(iaeType, previousLabel); 1389 code.invokeStatic(thrower, null, localC); 1390 code.loadConstant(localResult, "NONE"); 1391 code.returnValue(localResult); 1392 1393 code.mark(catchInner); 1394 code.loadConstant(localResult, "INNER"); 1395 code.returnValue(localResult); 1396 1397 code.mark(catchOuter); 1398 code.loadConstant(localResult, "OUTER"); 1399 code.returnValue(localResult); 1400 1401 Method method = getMethod(); 1402 assertEquals("OUTER", method.invoke(null, 1, 0, 0)); 1403 assertEquals("INNER", method.invoke(null, 0, 1, 0)); 1404 assertEquals("OUTER", method.invoke(null, 0, 0, 1)); 1405 assertEquals("NONE", method.invoke(null, 0, 0, 0)); 1406 } 1407 1408 public void testThrow() throws Exception { 1409 /* 1410 * public static void call() { 1411 * throw new IllegalStateException(); 1412 * } 1413 */ 1414 MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call"); 1415 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1416 TypeId<IllegalStateException> iseType = TypeId.get(IllegalStateException.class); 1417 MethodId<IllegalStateException, Void> iseConstructor = iseType.getConstructor(); 1418 Local<IllegalStateException> localIse = code.newLocal(iseType); 1419 code.newInstance(localIse, iseConstructor); 1420 code.throwValue(localIse); 1421 1422 try { 1423 getMethod().invoke(null); 1424 fail(); 1425 } catch (InvocationTargetException expected) { 1426 assertEquals(IllegalStateException.class, expected.getCause().getClass()); 1427 } 1428 } 1429 1430 public void testUnusedParameters() throws Exception { 1431 /* 1432 * public static void call(int unused1, long unused2, long unused3) {} 1433 */ 1434 MethodId<?, Void> methodId = GENERATED.getMethod( 1435 TypeId.VOID, "call", TypeId.INT, TypeId.LONG, TypeId.LONG); 1436 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1437 code.returnVoid(); 1438 getMethod().invoke(null, 1, 2, 3); 1439 } 1440 1441 public void testFloatingPointCompare() throws Exception { 1442 Method floatG = floatingPointCompareMethod(TypeId.FLOAT, 1); 1443 assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY)); 1444 assertEquals(-1, floatG.invoke(null, 1.0f, 2.0f)); 1445 assertEquals(0, floatG.invoke(null, 1.0f, 1.0f)); 1446 assertEquals(1, floatG.invoke(null, 2.0f, 1.0f)); 1447 assertEquals(1, floatG.invoke(null, 1.0f, Float.NaN)); 1448 assertEquals(1, floatG.invoke(null, Float.NaN, 1.0f)); 1449 assertEquals(1, floatG.invoke(null, Float.NaN, Float.NaN)); 1450 assertEquals(1, floatG.invoke(null, Float.NaN, Float.POSITIVE_INFINITY)); 1451 1452 Method floatL = floatingPointCompareMethod(TypeId.FLOAT, -1); 1453 assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY)); 1454 assertEquals(-1, floatL.invoke(null, 1.0f, 2.0f)); 1455 assertEquals(0, floatL.invoke(null, 1.0f, 1.0f)); 1456 assertEquals(1, floatL.invoke(null, 2.0f, 1.0f)); 1457 assertEquals(-1, floatL.invoke(null, 1.0f, Float.NaN)); 1458 assertEquals(-1, floatL.invoke(null, Float.NaN, 1.0f)); 1459 assertEquals(-1, floatL.invoke(null, Float.NaN, Float.NaN)); 1460 assertEquals(-1, floatL.invoke(null, Float.NaN, Float.POSITIVE_INFINITY)); 1461 1462 Method doubleG = floatingPointCompareMethod(TypeId.DOUBLE, 1); 1463 assertEquals(-1, doubleG.invoke(null, 1.0, Double.POSITIVE_INFINITY)); 1464 assertEquals(-1, doubleG.invoke(null, 1.0, 2.0)); 1465 assertEquals(0, doubleG.invoke(null, 1.0, 1.0)); 1466 assertEquals(1, doubleG.invoke(null, 2.0, 1.0)); 1467 assertEquals(1, doubleG.invoke(null, 1.0, Double.NaN)); 1468 assertEquals(1, doubleG.invoke(null, Double.NaN, 1.0)); 1469 assertEquals(1, doubleG.invoke(null, Double.NaN, Double.NaN)); 1470 assertEquals(1, doubleG.invoke(null, Double.NaN, Double.POSITIVE_INFINITY)); 1471 1472 Method doubleL = floatingPointCompareMethod(TypeId.DOUBLE, -1); 1473 assertEquals(-1, doubleL.invoke(null, 1.0, Double.POSITIVE_INFINITY)); 1474 assertEquals(-1, doubleL.invoke(null, 1.0, 2.0)); 1475 assertEquals(0, doubleL.invoke(null, 1.0, 1.0)); 1476 assertEquals(1, doubleL.invoke(null, 2.0, 1.0)); 1477 assertEquals(-1, doubleL.invoke(null, 1.0, Double.NaN)); 1478 assertEquals(-1, doubleL.invoke(null, Double.NaN, 1.0)); 1479 assertEquals(-1, doubleL.invoke(null, Double.NaN, Double.NaN)); 1480 assertEquals(-1, doubleL.invoke(null, Double.NaN, Double.POSITIVE_INFINITY)); 1481 } 1482 1483 private <T extends Number> Method floatingPointCompareMethod( 1484 TypeId<T> valueType, int nanValue) throws Exception { 1485 /* 1486 * public static int call(float a, float b) { 1487 * int result = a <=> b; 1488 * return result; 1489 * } 1490 */ 1491 reset(); 1492 MethodId<?, Integer> methodId = GENERATED.getMethod( 1493 TypeId.INT, "call", valueType, valueType); 1494 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1495 Local<T> localA = code.getParameter(0, valueType); 1496 Local<T> localB = code.getParameter(1, valueType); 1497 Local<Integer> localResult = code.newLocal(TypeId.INT); 1498 code.compareFloatingPoint(localResult, localA, localB, nanValue); 1499 code.returnValue(localResult); 1500 return getMethod(); 1501 } 1502 1503 public void testLongCompare() throws Exception { 1504 /* 1505 * public static int call(long a, long b) { 1506 * int result = a <=> b; 1507 * return result; 1508 * } 1509 */ 1510 MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.LONG, TypeId.LONG); 1511 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1512 Local<Long> localA = code.getParameter(0, TypeId.LONG); 1513 Local<Long> localB = code.getParameter(1, TypeId.LONG); 1514 Local<Integer> localResult = code.newLocal(TypeId.INT); 1515 code.compareLongs(localResult, localA, localB); 1516 code.returnValue(localResult); 1517 1518 Method method = getMethod(); 1519 assertEquals(0, method.invoke(null, Long.MIN_VALUE, Long.MIN_VALUE)); 1520 assertEquals(-1, method.invoke(null, Long.MIN_VALUE, 0)); 1521 assertEquals(-1, method.invoke(null, Long.MIN_VALUE, Long.MAX_VALUE)); 1522 assertEquals(1, method.invoke(null, 0, Long.MIN_VALUE)); 1523 assertEquals(0, method.invoke(null, 0, 0)); 1524 assertEquals(-1, method.invoke(null, 0, Long.MAX_VALUE)); 1525 assertEquals(1, method.invoke(null, Long.MAX_VALUE, Long.MIN_VALUE)); 1526 assertEquals(1, method.invoke(null, Long.MAX_VALUE, 0)); 1527 assertEquals(0, method.invoke(null, Long.MAX_VALUE, Long.MAX_VALUE)); 1528 } 1529 1530 public void testArrayLength() throws Exception { 1531 Method booleanArrayLength = arrayLengthMethod(BOOLEAN_ARRAY); 1532 assertEquals(0, booleanArrayLength.invoke(null, new Object[] { new boolean[0] })); 1533 assertEquals(5, booleanArrayLength.invoke(null, new Object[] { new boolean[5] })); 1534 1535 Method intArrayLength = arrayLengthMethod(INT_ARRAY); 1536 assertEquals(0, intArrayLength.invoke(null, new Object[] { new int[0] })); 1537 assertEquals(5, intArrayLength.invoke(null, new Object[] { new int[5] })); 1538 1539 Method longArrayLength = arrayLengthMethod(LONG_ARRAY); 1540 assertEquals(0, longArrayLength.invoke(null, new Object[] { new long[0] })); 1541 assertEquals(5, longArrayLength.invoke(null, new Object[] { new long[5] })); 1542 1543 Method objectArrayLength = arrayLengthMethod(OBJECT_ARRAY); 1544 assertEquals(0, objectArrayLength.invoke(null, new Object[] { new Object[0] })); 1545 assertEquals(5, objectArrayLength.invoke(null, new Object[] { new Object[5] })); 1546 1547 Method long2dArrayLength = arrayLengthMethod(LONG_2D_ARRAY); 1548 assertEquals(0, long2dArrayLength.invoke(null, new Object[] { new long[0][0] })); 1549 assertEquals(5, long2dArrayLength.invoke(null, new Object[] { new long[5][10] })); 1550 } 1551 1552 private <T> Method arrayLengthMethod(TypeId<T> valueType) throws Exception { 1553 /* 1554 * public static int call(long[] array) { 1555 * int result = array.length; 1556 * return result; 1557 * } 1558 */ 1559 reset(); 1560 MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", valueType); 1561 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1562 Local<T> localArray = code.getParameter(0, valueType); 1563 Local<Integer> localResult = code.newLocal(TypeId.INT); 1564 code.arrayLength(localResult, localArray); 1565 code.returnValue(localResult); 1566 return getMethod(); 1567 } 1568 1569 public void testNewArray() throws Exception { 1570 Method newBooleanArray = newArrayMethod(BOOLEAN_ARRAY); 1571 assertEquals("[]", Arrays.toString((boolean[]) newBooleanArray.invoke(null, 0))); 1572 assertEquals("[false, false, false]", 1573 Arrays.toString((boolean[]) newBooleanArray.invoke(null, 3))); 1574 1575 Method newIntArray = newArrayMethod(INT_ARRAY); 1576 assertEquals("[]", Arrays.toString((int[]) newIntArray.invoke(null, 0))); 1577 assertEquals("[0, 0, 0]", Arrays.toString((int[]) newIntArray.invoke(null, 3))); 1578 1579 Method newLongArray = newArrayMethod(LONG_ARRAY); 1580 assertEquals("[]", Arrays.toString((long[]) newLongArray.invoke(null, 0))); 1581 assertEquals("[0, 0, 0]", Arrays.toString((long[]) newLongArray.invoke(null, 3))); 1582 1583 Method newObjectArray = newArrayMethod(OBJECT_ARRAY); 1584 assertEquals("[]", Arrays.toString((Object[]) newObjectArray.invoke(null, 0))); 1585 assertEquals("[null, null, null]", 1586 Arrays.toString((Object[]) newObjectArray.invoke(null, 3))); 1587 1588 Method new2dLongArray = newArrayMethod(LONG_2D_ARRAY); 1589 assertEquals("[]", Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 0))); 1590 assertEquals("[null, null, null]", 1591 Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 3))); 1592 } 1593 1594 private <T> Method newArrayMethod(TypeId<T> valueType) throws Exception { 1595 /* 1596 * public static long[] call(int length) { 1597 * long[] result = new long[length]; 1598 * return result; 1599 * } 1600 */ 1601 reset(); 1602 MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", TypeId.INT); 1603 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1604 Local<Integer> localLength = code.getParameter(0, TypeId.INT); 1605 Local<T> localResult = code.newLocal(valueType); 1606 code.newArray(localResult, localLength); 1607 code.returnValue(localResult); 1608 return getMethod(); 1609 } 1610 1611 public void testReadAndWriteArray() throws Exception { 1612 Method swapBooleanArray = arraySwapMethod(BOOLEAN_ARRAY, TypeId.BOOLEAN); 1613 boolean[] booleans = new boolean[3]; 1614 assertEquals(false, swapBooleanArray.invoke(null, booleans, 1, true)); 1615 assertEquals("[false, true, false]", Arrays.toString(booleans)); 1616 1617 Method swapIntArray = arraySwapMethod(INT_ARRAY, TypeId.INT); 1618 int[] ints = new int[3]; 1619 assertEquals(0, swapIntArray.invoke(null, ints, 1, 5)); 1620 assertEquals("[0, 5, 0]", Arrays.toString(ints)); 1621 1622 Method swapLongArray = arraySwapMethod(LONG_ARRAY, TypeId.LONG); 1623 long[] longs = new long[3]; 1624 assertEquals(0L, swapLongArray.invoke(null, longs, 1, 6L)); 1625 assertEquals("[0, 6, 0]", Arrays.toString(longs)); 1626 1627 Method swapObjectArray = arraySwapMethod(OBJECT_ARRAY, TypeId.OBJECT); 1628 Object[] objects = new Object[3]; 1629 assertEquals(null, swapObjectArray.invoke(null, objects, 1, "X")); 1630 assertEquals("[null, X, null]", Arrays.toString(objects)); 1631 1632 Method swapLong2dArray = arraySwapMethod(LONG_2D_ARRAY, LONG_ARRAY); 1633 long[][] longs2d = new long[3][]; 1634 assertEquals(null, swapLong2dArray.invoke(null, longs2d, 1, new long[] { 7 })); 1635 assertEquals("[null, [7], null]", Arrays.deepToString(longs2d)); 1636 } 1637 1638 private <A, T> Method arraySwapMethod(TypeId<A> arrayType, TypeId<T> singleType) 1639 throws Exception { 1640 /* 1641 * public static long swap(long[] array, int index, long newValue) { 1642 * long result = array[index]; 1643 * array[index] = newValue; 1644 * return result; 1645 * } 1646 */ 1647 reset(); 1648 MethodId<?, T> methodId = GENERATED.getMethod( 1649 singleType, "call", arrayType, TypeId.INT, singleType); 1650 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1651 Local<A> localArray = code.getParameter(0, arrayType); 1652 Local<Integer> localIndex = code.getParameter(1, TypeId.INT); 1653 Local<T> localNewValue = code.getParameter(2, singleType); 1654 Local<T> localResult = code.newLocal(singleType); 1655 code.aget(localResult, localArray, localIndex); 1656 code.aput(localArray, localIndex, localNewValue); 1657 code.returnValue(localResult); 1658 return getMethod(); 1659 } 1660 1661 public void testSynchronizedFlagImpactsDeclarationOnly() throws Exception { 1662 /* 1663 * public synchronized void call() { 1664 * wait(100L); 1665 * } 1666 */ 1667 MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call"); 1668 MethodId<Object, Void> wait = TypeId.OBJECT.getMethod(TypeId.VOID, "wait", TypeId.LONG); 1669 Code code = dexMaker.declare(methodId, PUBLIC | SYNCHRONIZED); 1670 Local<?> thisLocal = code.getThis(GENERATED); 1671 Local<Long> timeout = code.newLocal(TypeId.LONG); 1672 code.loadConstant(timeout, 100L); 1673 code.invokeVirtual(wait, null, thisLocal, timeout); 1674 code.returnVoid(); 1675 1676 addDefaultConstructor(); 1677 1678 Class<?> generatedClass = generateAndLoad(); 1679 Object instance = generatedClass.newInstance(); 1680 Method method = generatedClass.getMethod("call"); 1681 assertTrue(Modifier.isSynchronized(method.getModifiers())); 1682 try { 1683 method.invoke(instance); 1684 fail(); 1685 } catch (InvocationTargetException expected) { 1686 assertTrue(expected.getCause() instanceof IllegalMonitorStateException); 1687 } 1688 } 1689 1690 public void testMonitorEnterMonitorExit() throws Exception { 1691 /* 1692 * public synchronized void call() { 1693 * synchronized (this) { 1694 * wait(100L); 1695 * } 1696 * } 1697 */ 1698 MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call"); 1699 MethodId<Object, Void> wait = TypeId.OBJECT.getMethod(TypeId.VOID, "wait", TypeId.LONG); 1700 Code code = dexMaker.declare(methodId, PUBLIC); 1701 Local<?> thisLocal = code.getThis(GENERATED); 1702 Local<Long> timeout = code.newLocal(TypeId.LONG); 1703 code.monitorEnter(thisLocal); 1704 code.loadConstant(timeout, 100L); 1705 code.invokeVirtual(wait, null, thisLocal, timeout); 1706 code.monitorExit(thisLocal); 1707 code.returnVoid(); 1708 1709 addDefaultConstructor(); 1710 1711 Class<?> generatedClass = generateAndLoad(); 1712 Object instance = generatedClass.newInstance(); 1713 Method method = generatedClass.getMethod("call"); 1714 assertFalse(Modifier.isSynchronized(method.getModifiers())); 1715 method.invoke(instance); // will take 100ms 1716 } 1717 1718 public void testMoveInt() throws Exception { 1719 /* 1720 * public static int call(int a) { 1721 * int b = a; 1722 * int c = a + b; 1723 * return c; 1724 * } 1725 */ 1726 MethodId<?, Integer> methodId = GENERATED.getMethod(TypeId.INT, "call", TypeId.INT); 1727 Code code = dexMaker.declare(methodId, PUBLIC | STATIC); 1728 Local<Integer> a = code.getParameter(0, TypeId.INT); 1729 Local<Integer> b = code.newLocal(TypeId.INT); 1730 Local<Integer> c = code.newLocal(TypeId.INT); 1731 code.move(b, a); 1732 code.op(BinaryOp.ADD, c, a, b); 1733 code.returnValue(c); 1734 1735 assertEquals(6, getMethod().invoke(null, 3)); 1736 } 1737 1738 public void testPrivateClassesAreUnsupported() { 1739 try { 1740 dexMaker.declare(TypeId.get("LPrivateClass;"), "PrivateClass.generated", PRIVATE, 1741 TypeId.OBJECT); 1742 fail(); 1743 } catch (IllegalArgumentException expected) { 1744 } 1745 } 1746 1747 public void testAbstractMethodsAreUnsupported() { 1748 MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call"); 1749 try { 1750 dexMaker.declare(methodId, ABSTRACT); 1751 fail(); 1752 } catch (IllegalArgumentException expected) { 1753 } 1754 } 1755 1756 public void testNativeMethodsAreUnsupported() { 1757 MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call"); 1758 try { 1759 dexMaker.declare(methodId, NATIVE); 1760 fail(); 1761 } catch (IllegalArgumentException expected) { 1762 } 1763 } 1764 1765 public void testSynchronizedFieldsAreUnsupported() { 1766 try { 1767 FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "synchronizedField"); 1768 dexMaker.declare(fieldId, SYNCHRONIZED, null); 1769 fail(); 1770 } catch (IllegalArgumentException expected) { 1771 } 1772 } 1773 1774 public void testInitialValueWithNonStaticField() { 1775 try { 1776 FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "nonStaticField"); 1777 dexMaker.declare(fieldId, 0, 1); 1778 fail(); 1779 } catch (IllegalArgumentException expected) { 1780 } 1781 } 1782 1783 // TODO: cast primitive to non-primitive 1784 // TODO: cast non-primitive to primitive 1785 // TODO: cast byte to integer 1786 // TODO: cast byte to long 1787 // TODO: cast long to byte 1788 // TODO: fail if a label is unreachable (never navigated to) 1789 // TODO: more strict type parameters: Integer on methods 1790 // TODO: don't generate multiple times (?) 1791 // TODO: test array types 1792 // TODO: test generating an interface 1793 // TODO: declare native method or abstract method 1794 // TODO: get a thrown exception 'e' into a local 1795 // TODO: move a primitive or reference 1796 1797 private void addDefaultConstructor() { 1798 Code code = dexMaker.declare(GENERATED.getConstructor(), PUBLIC); 1799 Local<?> thisRef = code.getThis(GENERATED); 1800 code.invokeDirect(TypeId.OBJECT.getConstructor(), null, thisRef); 1801 code.returnVoid(); 1802 } 1803 1804 /** 1805 * Returns the generated method. 1806 */ 1807 private Method getMethod() throws Exception { 1808 Class<?> generated = generateAndLoad(); 1809 for (Method method : generated.getMethods()) { 1810 if (method.getName().equals("call")) { 1811 return method; 1812 } 1813 } 1814 throw new IllegalStateException("no call() method"); 1815 } 1816 1817 public static File getDataDirectory() throws Exception { 1818 Class<?> environmentClass = Class.forName("android.os.Environment"); 1819 Method method = environmentClass.getMethod("getDataDirectory"); 1820 Object dataDirectory = method.invoke(null); 1821 return (File) dataDirectory; 1822 } 1823 1824 private Class<?> generateAndLoad() throws Exception { 1825 return dexMaker.generateAndLoad(getClass().getClassLoader(), getDataDirectory()) 1826 .loadClass("Generated"); 1827 } 1828 } 1829