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