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