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