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