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