diff options
author | Jesse Wilson <jessewilson@google.com> | 2011-04-15 14:28:02 -0700 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2011-04-16 07:19:47 -0700 |
commit | da2a9cbd9df97adab3053c9831279052e490e3ac (patch) | |
tree | 3c31bd6bfb2422046f964629b5d4355f2d0b39ba | |
parent | b665fb24d785e27480cc9b8debcf9edf4ac65d2c (diff) | |
download | android_dalvik-da2a9cbd9df97adab3053c9831279052e490e3ac.tar.gz android_dalvik-da2a9cbd9df97adab3053c9831279052e490e3ac.tar.bz2 android_dalvik-da2a9cbd9df97adab3053c9831279052e490e3ac.zip |
Return Code instances from Method.declare.
Add type cast, and instanceof.
Add a unit test for recursive method calls.
Add return type checking.
Improve documentation on BinaryOp.
Change-Id: Ib85ed81be90756793067913efe86e1e442483f76
http://code.google.com/p/android/issues/detail?id=6322
-rw-r--r-- | dx/junit-tests/HelloWorldMaker.java | 7 | ||||
-rw-r--r-- | dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java | 465 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/BinaryOp.java | 36 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Code.java | 134 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/DexGenerator.java | 20 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Field.java | 4 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Local.java | 29 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Method.java | 26 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Type.java | 6 |
9 files changed, 432 insertions, 295 deletions
diff --git a/dx/junit-tests/HelloWorldMaker.java b/dx/junit-tests/HelloWorldMaker.java index 6c6cbffec..1f0008b75 100644 --- a/dx/junit-tests/HelloWorldMaker.java +++ b/dx/junit-tests/HelloWorldMaker.java @@ -57,10 +57,11 @@ public class HelloWorldMaker { Field<System, PrintStream> systemOutField = system.getField(printStream, "out"); Method<Integer, String> toHexString = integer.getMethod(string, "toHexString", intType); Method<PrintStream, Void> println = printStream.getMethod(voidType, "println", string); + Method hello = helloWorld.getMethod(voidType, "hello"); // create some registers // (I'd like a better syntax for this) - Code code = generator.newCode(); + Code code = hello.declare(AccessFlags.ACC_STATIC | AccessFlags.ACC_PUBLIC); Local<Integer> a = code.newLocal(intType); Local<Integer> b = code.newLocal(intType); Local<Integer> c = code.newLocal(intType); @@ -76,10 +77,6 @@ public class HelloWorldMaker { code.invokeVirtual(println, null, localSystemOut, s); code.returnVoid(); - // wrap it up by building the HelloWorld class and hello() method - Method hello = helloWorld.getMethod(voidType, "hello"); - hello.declare(AccessFlags.ACC_STATIC | AccessFlags.ACC_PUBLIC, code); - // TODO: create the constructor helloWorld.declare("Generated.java", AccessFlags.ACC_PUBLIC, object); diff --git a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java b/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java index c94e9b1e6..8a9a669ab 100644 --- a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java +++ b/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java @@ -24,9 +24,7 @@ import static com.android.dx.rop.code.AccessFlags.ACC_PUBLIC; import static com.android.dx.rop.code.AccessFlags.ACC_STATIC; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.Callable; import junit.framework.TestCase; @@ -38,25 +36,12 @@ import junit.framework.TestCase; * <p>This test must run on a Dalvik VM. */ public final class DexGeneratorTest extends TestCase { - private static final Map<Class<?>, Class<?>> BOXED_TO_PRIMITIVE - = new HashMap<Class<?>, Class<?>>(); - static { - BOXED_TO_PRIMITIVE.put(Boolean.class, boolean.class); - BOXED_TO_PRIMITIVE.put(Byte.class, byte.class); - BOXED_TO_PRIMITIVE.put(Character.class, char.class); - BOXED_TO_PRIMITIVE.put(Double.class, double.class); - BOXED_TO_PRIMITIVE.put(Float.class, float.class); - BOXED_TO_PRIMITIVE.put(Integer.class, int.class); - BOXED_TO_PRIMITIVE.put(Long.class, long.class); - BOXED_TO_PRIMITIVE.put(Short.class, short.class); - BOXED_TO_PRIMITIVE.put(Void.class, void.class); - } - private DexGenerator generator; private Type<Integer> intType; private Type<Long> longType; private Type<Boolean> booleanType; private Type<Object> objectType; + private Type<String> stringType; private Type<DexGeneratorTest> dexGeneratorTestType; private Type<?> generatedType; private Type<Callable> callableType; @@ -77,33 +62,33 @@ public final class DexGeneratorTest extends TestCase { longType = generator.getType(long.class); booleanType = generator.getType(boolean.class); objectType = generator.getType(Object.class); + stringType = generator.getType(String.class); dexGeneratorTestType = generator.getType(DexGeneratorTest.class); generatedType = generator.getType("LGenerated;"); callableType = generator.getType(Callable.class); call = callableType.getMethod(objectType, "call"); + generatedType.declare("Generated.java", ACC_PUBLIC, objectType); } public void testNewInstance() throws Exception { /* - * public static Constructable generatedMethod(long a, boolean b) { + * public static Constructable call(long a, boolean b) { * Constructable result = new Constructable(a, b); * return result; * } */ - - Code code = generator.newCode(); - Local<Long> localA = code.newParameter(longType); - Local<Boolean> localB = code.newParameter(booleanType); Type<Constructable> constructable = generator.getType(Constructable.class); + Code code = generatedType.getMethod(constructable, "call", longType, booleanType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Long> localA = code.getParameter(0, longType); + Local<Boolean> localB = code.getParameter(1, booleanType); Method<Constructable, Void> constructor = constructable.getConstructor(longType, booleanType); Local<Constructable> localResult = code.newLocal(constructable); code.newInstance(localResult, constructor, localA, localB); code.returnValue(localResult); - java.lang.reflect.Method method - = newMethod(Constructable.class, code, long.class, boolean.class); - Constructable constructed = (Constructable) method.invoke(null, 5L, false); + Constructable constructed = (Constructable) getMethod().invoke(null, 5L, false); assertEquals(5L, constructed.a); assertEquals(false, constructed.b); } @@ -119,22 +104,21 @@ public final class DexGeneratorTest extends TestCase { public void testInvokeStatic() throws Exception { /* - * public static int generatedMethod(int a) { + * public static int call(int a) { * int result = DexGeneratorTest.staticMethod(a); * return result; * } */ - - Code code = generator.newCode(); - Local<Integer> localA = code.newParameter(intType); + Code code = generatedType.getMethod(intType, "call", intType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Integer> localA = code.getParameter(0, intType); Local<Integer> localResult = code.newLocal(intType); - Method<DexGeneratorTest, Integer> staticMethod + Method<?, Integer> staticMethod = dexGeneratorTestType.getMethod(intType, "staticMethod", intType); code.invokeStatic(staticMethod, localResult, localA); code.returnValue(localResult); - java.lang.reflect.Method method = newMethod(int.class, code, int.class); - assertEquals(10, method.invoke(null, 4)); + assertEquals(10, getMethod().invoke(null, 4)); } @SuppressWarnings("unused") // called by generated code @@ -144,23 +128,22 @@ public final class DexGeneratorTest extends TestCase { public void testInvokeVirtual() throws Exception { /* - * public static int generatedMethod(DexGeneratorTest test, int a) { + * public static int call(DexGeneratorTest test, int a) { * int result = test.virtualMethod(a); * return result; * } */ - Code code = generator.newCode(); - Local<DexGeneratorTest> localInstance = code.newParameter(dexGeneratorTestType); - Local<Integer> localA = code.newParameter(intType); + Code code = generatedType.getMethod(intType, "call", dexGeneratorTestType, intType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<DexGeneratorTest> localInstance = code.getParameter(0, dexGeneratorTestType); + Local<Integer> localA = code.getParameter(1, intType); Local<Integer> localResult = code.newLocal(intType); Method<DexGeneratorTest, Integer> virtualMethod = dexGeneratorTestType.getMethod(intType, "virtualMethod", intType); code.invokeVirtual(virtualMethod, localResult, localInstance, localA); code.returnValue(localResult); - java.lang.reflect.Method method = newMethod( - int.class, code, DexGeneratorTest.class, int.class); - assertEquals(9, method.invoke(null, this, 4)); + assertEquals(9, getMethod().invoke(null, this, 4)); } @SuppressWarnings("unused") // called by generated code @@ -168,44 +151,42 @@ public final class DexGeneratorTest extends TestCase { return a + 5; } - public void testInvokeDirect() throws Exception { + public <G> void testInvokeDirect() throws Exception { /* * private int directMethod() { * int a = 5; * return a; * } * - * public static int generatedMethod(Generated g) { + * public static int call(Generated g) { * int b = g.directMethod(); * return b; * } */ - Method<?, Integer> directMethod = generatedType.getMethod(intType, "directMethod"); - Code directCode = generator.newCode(); - directCode.newThisLocal(generatedType); // 'this' is unused + Type<G> generated = generator.getType("LGenerated;"); + Method<G, Integer> directMethod = generated.getMethod(intType, "directMethod"); + Code directCode = directMethod.declare(ACC_PRIVATE); + directCode.getThis(generated); // 'this' is unused Local<Integer> localA = directCode.newLocal(intType); directCode.loadConstant(localA, 5); directCode.returnValue(localA); - directMethod.declare(ACC_PRIVATE, directCode); - Method<?, ?> method = generatedType.getMethod(intType, "generatedMethod", generatedType); - Code code = generator.newCode(); + Method<G, Integer> method = generated.getMethod(intType, "call", generated); + Code code = method.declare(ACC_PUBLIC | ACC_STATIC); Local<Integer> localB = code.newLocal(intType); - Local<?> localG = code.newParameter(generatedType); + Local<G> localG = code.getParameter(0, generated); code.invokeDirect(directMethod, localB, localG); code.returnValue(localB); - method.declare(ACC_PUBLIC | ACC_STATIC, code); addDefaultConstructor(); - generatedType.declare("Generated.java", ACC_PUBLIC, objectType); Class<?> generatedClass = loadAndGenerate(); Object instance = generatedClass.newInstance(); - java.lang.reflect.Method m = generatedClass.getMethod("generatedMethod", generatedClass); + java.lang.reflect.Method m = generatedClass.getMethod("call", generatedClass); assertEquals(5, m.invoke(null, instance)); } - public void testInvokeSuper() throws Exception { + public <G> void testInvokeSuper() throws Exception { /* * public int superHashCode() { * int result = super.hashCode(); @@ -215,24 +196,21 @@ public final class DexGeneratorTest extends TestCase { * return 0; * } */ - Method<?, Integer> objectHashCode = objectType.getMethod(intType, "hashCode"); - Code superHashCode = generator.newCode(); + Type<G> generated = generator.getType("LGenerated;"); + Method<Object, Integer> objectHashCode = objectType.getMethod(intType, "hashCode"); + Code superHashCode = generated.getMethod(intType, "superHashCode").declare(ACC_PUBLIC); Local<Integer> localResult = superHashCode.newLocal(intType); - Local<?> localThis = superHashCode.newThisLocal(generatedType); + Local<G> localThis = superHashCode.getThis(generated); superHashCode.invokeSuper(objectHashCode, localResult, localThis); superHashCode.returnValue(localResult); - generatedType.getMethod(intType, "superHashCode").declare(ACC_PUBLIC, superHashCode); - Code generatedHashCode = generator.newCode(); - generatedHashCode.newThisLocal(generatedType); + Code generatedHashCode = generated.getMethod(intType, "hashCode").declare(ACC_PUBLIC); Local<Integer> localZero = generatedHashCode.newLocal(intType); generatedHashCode.loadConstant(localZero, 0); generatedHashCode.returnValue(localZero); - generatedType.getMethod(intType, "hashCode").declare(ACC_PUBLIC, generatedHashCode); addDefaultConstructor(); - generatedType.declare("Generated.java", ACC_PUBLIC, objectType); Class<?> generatedClass = loadAndGenerate(); Object instance = generatedClass.newInstance(); java.lang.reflect.Method m = generatedClass.getMethod("superHashCode"); @@ -246,13 +224,14 @@ public final class DexGeneratorTest extends TestCase { public void testInvokeInterface() throws Exception { /* - * public static Object generatedMethod(Callable c) { + * public static Object call(Callable c) { * Object result = c.call(); * return result; * } */ - Code code = generator.newCode(); - Local<Callable> localC = code.newParameter(callableType); + Code code = generatedType.getMethod(objectType, "call", callableType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Callable> localC = code.getParameter(0, callableType); Local<Object> localResult = code.newLocal(objectType); code.invokeInterface(call, localResult, localC); code.returnValue(localResult); @@ -262,30 +241,63 @@ public final class DexGeneratorTest extends TestCase { return "abc"; } }; - java.lang.reflect.Method method = newMethod(Object.class, code, Callable.class); - assertEquals("abc", method.invoke(null, callable)); + assertEquals("abc", getMethod().invoke(null, callable)); } public void testParameterMismatch() throws Exception { - Code code = generator.newCode(); - code.newParameter(intType); - code.newParameter(objectType); - Type<?>[] argTypes = { generator.getType(Integer.class), // should fail because the code specifies int objectType, }; - - Method<?, Integer> method = generatedType.getMethod(intType, "generatedMethod", argTypes); + Method<?, Integer> method = generatedType.getMethod(intType, "call", argTypes); + Code code = method.declare(ACC_PUBLIC | ACC_STATIC); try { - method.declare(ACC_PUBLIC | ACC_STATIC, code); - fail(); + code.getParameter(0, intType); } catch (IllegalArgumentException e) { } + try { + code.getParameter(2, intType); + } catch (IndexOutOfBoundsException e) { + } + } + + public void testInvokeTypeSafety() throws Exception { + /* + * public static boolean call(DexGeneratorTest test) { + * CharSequence cs = test.toString(); + * boolean result = cs.equals(test); + * return result; + * } + */ + Code code = generatedType.getMethod(booleanType, "call", dexGeneratorTestType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<DexGeneratorTest> localTest = code.getParameter(0, dexGeneratorTestType); + Type<CharSequence> charSequenceType = generator.getType(CharSequence.class); + Method<Object, String> objectToString = objectType.getMethod(stringType, "toString"); + Method<Object, Boolean> objectEquals + = objectType.getMethod(booleanType, "equals", objectType); + Local<CharSequence> localCs = code.newLocal(charSequenceType); + Local<Boolean> localResult = code.newLocal(booleanType); + code.invokeVirtual(objectToString, localCs, localTest); + code.invokeVirtual(objectEquals, localResult, localCs, localTest); + code.returnValue(localResult); + + assertEquals(false, getMethod().invoke(null, this)); } public void testReturnTypeMismatch() { - fail("TODO"); + Code code = generatedType.getMethod(stringType, "call") + .declare(ACC_PUBLIC | ACC_STATIC); + try { + code.returnValue(code.newLocal(booleanType)); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + code.returnVoid(); + fail(); + } catch (IllegalArgumentException expected) { + } } public void testDeclareStaticFields() throws Exception { @@ -297,8 +309,6 @@ public final class DexGeneratorTest extends TestCase { */ generatedType.getField(intType, "a").declare(ACC_PUBLIC | ACC_STATIC, 3); generatedType.getField(objectType, "b").declare(ACC_PROTECTED | ACC_STATIC, null); - generatedType.declare("Generated.java", ACC_PUBLIC, objectType); - Class<?> generatedClass = loadAndGenerate(); java.lang.reflect.Field a = generatedClass.getField("a"); @@ -320,7 +330,7 @@ public final class DexGeneratorTest extends TestCase { */ generatedType.getField(intType, "a").declare(ACC_PUBLIC, null); generatedType.getField(objectType, "b").declare(ACC_PROTECTED, null); - generatedType.declare("Generated.java", ACC_PUBLIC, objectType); + addDefaultConstructor(); Class<?> generatedClass = loadAndGenerate(); @@ -340,7 +350,7 @@ public final class DexGeneratorTest extends TestCase { * Declare a constructor that takes an int parameter and assigns it to a * field. */ - public void testDeclareConstructor() throws Exception { + public <G> void testDeclareConstructor() throws Exception { /* * class Generated { * public final int a; @@ -349,17 +359,15 @@ public final class DexGeneratorTest extends TestCase { * } * } */ - Field<?, Integer> field = generatedType.getField(intType, "a"); + Type<G> generated = generator.getType("LGenerated;"); + Field<G, Integer> field = generated.getField(intType, "a"); field.declare(ACC_PUBLIC | ACC_FINAL, null); - - Code constructor = generator.newCode(); - Local<?> thisRef = constructor.newThisLocal(generatedType); - Local<Integer> parameter = constructor.newParameter(intType); - constructor.invokeDirect(objectType.getConstructor(), null, thisRef); - constructor.iput(field, (Local) thisRef, parameter); // TODO: type safety hurts us here - constructor.returnVoid(); - generatedType.getConstructor(intType).declare(ACC_PUBLIC | ACC_CONSTRUCTOR, constructor); - generatedType.declare("Generated.java", ACC_PUBLIC, objectType); + Code code = generatedType.getConstructor(intType).declare(ACC_PUBLIC | ACC_CONSTRUCTOR); + Local<G> thisRef = code.getThis(generated); + Local<Integer> parameter = code.getParameter(0, intType); + code.invokeDirect(objectType.getConstructor(), null, thisRef); + code.iput(field, thisRef, parameter); + code.returnVoid(); Class<?> generatedClass = loadAndGenerate(); java.lang.reflect.Field a = generatedClass.getField("a"); @@ -383,14 +391,15 @@ public final class DexGeneratorTest extends TestCase { private <T> void testReturnType(Class<T> javaType, T value) throws Exception { /* - * public int generatedMethod() { + * public int call() { * int a = 5; * return a; * } */ reset(); Type<T> returnType = generator.getType(javaType); - Code code = generator.newCode(); + Code code = generatedType.getMethod(returnType, "call") + .declare(ACC_PUBLIC | ACC_STATIC); if (value != null) { Local<T> i = code.newLocal(returnType); code.loadConstant(i, value); @@ -398,12 +407,9 @@ public final class DexGeneratorTest extends TestCase { } else { code.returnVoid(); } - generatedType.getMethod(returnType, "generatedMethod") - .declare(ACC_PUBLIC | ACC_STATIC, code); - generatedType.declare("Generated.java", ACC_PUBLIC, objectType); Class<?> generatedClass = loadAndGenerate(); - java.lang.reflect.Method method = generatedClass.getMethod("generatedMethod"); + java.lang.reflect.Method method = generatedClass.getMethod("call"); assertEquals(javaType, method.getReturnType()); assertEquals(value, method.invoke(null)); } @@ -442,7 +448,7 @@ public final class DexGeneratorTest extends TestCase { private java.lang.reflect.Method newBranchingMethod(Comparison comparison) throws Exception { /* - * public static int generatedMethod(int localA, int localB) { + * public static boolean call(int localA, int localB) { * if (a comparison b) { * return true; * } @@ -450,9 +456,10 @@ public final class DexGeneratorTest extends TestCase { * } */ reset(); - Code code = generator.newCode(); - Local<Integer> localA = code.newParameter(intType); - Local<Integer> localB = code.newParameter(intType); + Code code = generatedType.getMethod(booleanType, "call", intType, intType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Integer> localA = code.getParameter(0, intType); + Local<Integer> localB = code.getParameter(1, intType); Local<Boolean> result = code.newLocal(generator.getType(boolean.class)); Label afterIf = code.newLabel(); Label ifBody = code.newLabel(); @@ -466,57 +473,57 @@ public final class DexGeneratorTest extends TestCase { code.mark(afterIf); code.loadConstant(result, false); code.returnValue(result); - return newMethod(boolean.class, code, int.class, int.class); + return getMethod(); } public void testCastIntegerToInteger() throws Exception { - java.lang.reflect.Method intToLong = newCastingMethod(int.class, long.class); + java.lang.reflect.Method intToLong = newNumericCastingMethod(int.class, long.class); assertEquals(0x0000000000000000L, intToLong.invoke(null, 0x00000000)); assertEquals(0x000000007fffffffL, intToLong.invoke(null, 0x7fffffff)); assertEquals(0xffffffff80000000L, intToLong.invoke(null, 0x80000000)); assertEquals(0xffffffffffffffffL, intToLong.invoke(null, 0xffffffff)); - java.lang.reflect.Method longToInt = newCastingMethod(long.class, int.class); + java.lang.reflect.Method longToInt = newNumericCastingMethod(long.class, int.class); assertEquals(0x1234abcd, longToInt.invoke(null, 0x000000001234abcdL)); assertEquals(0x1234abcd, longToInt.invoke(null, 0x123456781234abcdL)); assertEquals(0x1234abcd, longToInt.invoke(null, 0xffffffff1234abcdL)); - java.lang.reflect.Method intToShort = newCastingMethod(int.class, short.class); + java.lang.reflect.Method intToShort = newNumericCastingMethod(int.class, short.class); assertEquals((short) 0x1234, intToShort.invoke(null, 0x00001234)); assertEquals((short) 0x1234, intToShort.invoke(null, 0xabcd1234)); assertEquals((short) 0x1234, intToShort.invoke(null, 0xffff1234)); - java.lang.reflect.Method intToChar = newCastingMethod(int.class, char.class); + java.lang.reflect.Method intToChar = newNumericCastingMethod(int.class, char.class); assertEquals((char) 0x1234, intToChar.invoke(null, 0x00001234)); assertEquals((char) 0x1234, intToChar.invoke(null, 0xabcd1234)); assertEquals((char) 0x1234, intToChar.invoke(null, 0xffff1234)); - java.lang.reflect.Method intToByte = newCastingMethod(int.class, byte.class); + java.lang.reflect.Method intToByte = newNumericCastingMethod(int.class, byte.class); assertEquals((byte) 0x34, intToByte.invoke(null, 0x00000034)); assertEquals((byte) 0x34, intToByte.invoke(null, 0xabcd1234)); assertEquals((byte) 0x34, intToByte.invoke(null, 0xffffff34)); } public void testCastIntegerToFloatingPoint() throws Exception { - java.lang.reflect.Method intToFloat = newCastingMethod(int.class, float.class); + java.lang.reflect.Method intToFloat = newNumericCastingMethod(int.class, float.class); assertEquals(0.0f, intToFloat.invoke(null, 0)); assertEquals(-1.0f, intToFloat.invoke(null, -1)); assertEquals(16777216f, intToFloat.invoke(null, 16777216)); assertEquals(16777216f, intToFloat.invoke(null, 16777217)); // precision - java.lang.reflect.Method intToDouble = newCastingMethod(int.class, double.class); + java.lang.reflect.Method intToDouble = newNumericCastingMethod(int.class, double.class); assertEquals(0.0, intToDouble.invoke(null, 0)); assertEquals(-1.0, intToDouble.invoke(null, -1)); assertEquals(16777216.0, intToDouble.invoke(null, 16777216)); assertEquals(16777217.0, intToDouble.invoke(null, 16777217)); - java.lang.reflect.Method longToFloat = newCastingMethod(long.class, float.class); + java.lang.reflect.Method longToFloat = newNumericCastingMethod(long.class, float.class); assertEquals(0.0f, longToFloat.invoke(null, 0L)); assertEquals(-1.0f, longToFloat.invoke(null, -1L)); assertEquals(16777216f, longToFloat.invoke(null, 16777216L)); assertEquals(16777216f, longToFloat.invoke(null, 16777217L)); - java.lang.reflect.Method longToDouble = newCastingMethod(long.class, double.class); + java.lang.reflect.Method longToDouble = newNumericCastingMethod(long.class, double.class); assertEquals(0.0, longToDouble.invoke(null, 0L)); assertEquals(-1.0, longToDouble.invoke(null, -1L)); assertEquals(9007199254740992.0, longToDouble.invoke(null, 9007199254740992L)); @@ -524,7 +531,7 @@ public final class DexGeneratorTest extends TestCase { } public void testCastFloatingPointToInteger() throws Exception { - java.lang.reflect.Method floatToInt = newCastingMethod(float.class, int.class); + java.lang.reflect.Method floatToInt = newNumericCastingMethod(float.class, int.class); assertEquals(0, floatToInt.invoke(null, 0.0f)); assertEquals(-1, floatToInt.invoke(null, -1.0f)); assertEquals(Integer.MAX_VALUE, floatToInt.invoke(null, 10e15f)); @@ -532,7 +539,7 @@ public final class DexGeneratorTest extends TestCase { assertEquals(Integer.MIN_VALUE, floatToInt.invoke(null, Float.NEGATIVE_INFINITY)); assertEquals(0, floatToInt.invoke(null, Float.NaN)); - java.lang.reflect.Method floatToLong = newCastingMethod(float.class, long.class); + java.lang.reflect.Method floatToLong = newNumericCastingMethod(float.class, long.class); assertEquals(0L, floatToLong.invoke(null, 0.0f)); assertEquals(-1L, floatToLong.invoke(null, -1.0f)); assertEquals(10000000272564224L, floatToLong.invoke(null, 10e15f)); @@ -540,7 +547,7 @@ public final class DexGeneratorTest extends TestCase { assertEquals(Long.MIN_VALUE, floatToLong.invoke(null, Float.NEGATIVE_INFINITY)); assertEquals(0L, floatToLong.invoke(null, Float.NaN)); - java.lang.reflect.Method doubleToInt = newCastingMethod(double.class, int.class); + java.lang.reflect.Method doubleToInt = newNumericCastingMethod(double.class, int.class); assertEquals(0, doubleToInt.invoke(null, 0.0)); assertEquals(-1, doubleToInt.invoke(null, -1.0)); assertEquals(Integer.MAX_VALUE, doubleToInt.invoke(null, 10e15)); @@ -548,7 +555,7 @@ public final class DexGeneratorTest extends TestCase { assertEquals(Integer.MIN_VALUE, doubleToInt.invoke(null, Double.NEGATIVE_INFINITY)); assertEquals(0, doubleToInt.invoke(null, Double.NaN)); - java.lang.reflect.Method doubleToLong = newCastingMethod(double.class, long.class); + java.lang.reflect.Method doubleToLong = newNumericCastingMethod(double.class, long.class); assertEquals(0L, doubleToLong.invoke(null, 0.0)); assertEquals(-1L, doubleToLong.invoke(null, -1.0)); assertEquals(10000000000000000L, doubleToLong.invoke(null, 10e15)); @@ -558,14 +565,14 @@ public final class DexGeneratorTest extends TestCase { } public void testCastFloatingPointToFloatingPoint() throws Exception { - java.lang.reflect.Method floatToDouble = newCastingMethod(float.class, double.class); + java.lang.reflect.Method floatToDouble = newNumericCastingMethod(float.class, double.class); assertEquals(0.0, floatToDouble.invoke(null, 0.0f)); assertEquals(-1.0, floatToDouble.invoke(null, -1.0f)); assertEquals(0.5, floatToDouble.invoke(null, 0.5f)); assertEquals(Double.NEGATIVE_INFINITY, floatToDouble.invoke(null, Float.NEGATIVE_INFINITY)); assertEquals(Double.NaN, floatToDouble.invoke(null, Float.NaN)); - java.lang.reflect.Method doubleToFloat = newCastingMethod(double.class, float.class); + java.lang.reflect.Method doubleToFloat = newNumericCastingMethod(double.class, float.class); assertEquals(0.0f, doubleToFloat.invoke(null, 0.0)); assertEquals(-1.0f, doubleToFloat.invoke(null, -1.0)); assertEquals(0.5f, doubleToFloat.invoke(null, 0.5)); @@ -573,10 +580,10 @@ public final class DexGeneratorTest extends TestCase { assertEquals(Float.NaN, doubleToFloat.invoke(null, Double.NaN)); } - private java.lang.reflect.Method newCastingMethod(Class<?> source, Class<?> target) + private java.lang.reflect.Method newNumericCastingMethod(Class<?> source, Class<?> target) throws Exception { /* - * public static short generatedMethod(int source) { + * public static short call(int source) { * short casted = (short) source; * return casted; * } @@ -584,12 +591,13 @@ public final class DexGeneratorTest extends TestCase { reset(); Type<?> sourceType = generator.getType(source); Type<?> targetType = generator.getType(target); - Code code = generator.newCode(); - Local<?> localSource = code.newParameter(sourceType); + Code code = generatedType.getMethod(targetType, "call", sourceType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<?> localSource = code.getParameter(0, sourceType); Local<?> localCasted = code.newLocal(targetType); - code.cast(localSource, localCasted); + code.numericCast(localSource, localCasted); code.returnValue(localCasted); - return newMethod(target, code, source); + return getMethod(); } public void testNot() throws Exception { @@ -604,20 +612,21 @@ public final class DexGeneratorTest extends TestCase { assertEquals(0x98765432edcba987L, notLong.invoke(null, 0x6789abcd12345678L)); } - private java.lang.reflect.Method newNotMethod(Class<?> source) throws Exception { + private <T> java.lang.reflect.Method newNotMethod(Class<T> source) throws Exception { /* - * public static short generatedMethod(int source) { + * public static short call(int source) { * source = ~source; * return not; * } */ reset(); - Type<?> sourceType = generator.getType(source); - Code code = generator.newCode(); - Local<?> localSource = code.newParameter(sourceType); - code.not((Local) localSource, localSource); // TODO: type safety + Type<T> valueType = generator.getType(source); + Code code = generatedType.getMethod(valueType, "call", valueType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<T> localSource = code.getParameter(0, valueType); + code.not(localSource, localSource); code.returnValue(localSource); - return newMethod(source, code, source); + return getMethod(); } public void testNegate() throws Exception { @@ -644,20 +653,21 @@ public final class DexGeneratorTest extends TestCase { assertEquals(Double.POSITIVE_INFINITY, negateDouble.invoke(null, Double.NEGATIVE_INFINITY)); } - private java.lang.reflect.Method newNegateMethod(Class<?> source) throws Exception { + private <T> java.lang.reflect.Method newNegateMethod(Class<T> source) throws Exception { /* - * public static short generatedMethod(int source) { + * public static short call(int source) { * source = -source; * return not; * } */ reset(); - Type<?> sourceType = generator.getType(source); - Code code = generator.newCode(); - Local<?> localSource = code.newParameter(sourceType); - code.negate((Local) localSource, localSource); // TODO: type safety + Type<T> valueType = generator.getType(source); + Code code = generatedType.getMethod(valueType, "call", valueType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<T> localSource = code.getParameter(0, valueType); + code.negate(localSource, localSource); code.returnValue(localSource); - return newMethod(source, code, source); + return getMethod(); } public void testIntBinaryOps() throws Exception { @@ -796,7 +806,7 @@ public final class DexGeneratorTest extends TestCase { assertEquals(Double.NaN, remainder.invoke(null, 5.5, 0.0)); } - private java.lang.reflect.Method newBinaryOpMethod(Class<?> valueClass, BinaryOp op) + private <T> java.lang.reflect.Method newBinaryOpMethod(Class<T> valueClass, BinaryOp op) throws Exception { /* * public static int binaryOp(int a, int b) { @@ -805,14 +815,15 @@ public final class DexGeneratorTest extends TestCase { * } */ reset(); - Type<?> valueType = generator.getType(valueClass); - Code code = generator.newCode(); - Local<?> localA = code.newParameter(valueType); - Local<?> localB = code.newParameter(valueType); - Local<?> localResult = code.newLocal(valueType); - code.op(op, (Local) localResult, (Local) localA, (Local) localB); // TODO: type safety + Type<T> valueType = generator.getType(valueClass); + Code code = generatedType.getMethod(valueType, "call", valueType,valueType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<T> localA = code.getParameter(0, valueType); + Local<T> localB = code.getParameter(1, valueType); + Local<T> localResult = code.newLocal(valueType); + code.op(op, localResult, localA, localB); code.returnValue(localResult); - return newMethod(valueClass, code, valueClass, valueClass); + return getMethod(); } public void testReadAndWriteInstanceFields() throws Exception { @@ -879,7 +890,7 @@ public final class DexGeneratorTest extends TestCase { private <V> java.lang.reflect.Method newInstanceSwapMethod( Class<V> valueClass, String fieldName) throws Exception { /* - * public static int generatedMethod(Instance instance, int newValue) { + * public static int call(Instance instance, int newValue) { * int oldValue = instance.intValue; * instance.intValue = newValue; * return oldValue; @@ -889,14 +900,15 @@ public final class DexGeneratorTest extends TestCase { Type<V> valueType = generator.getType(valueClass); Type<Instance> objectType = generator.getType(Instance.class); Field<Instance, V> field = objectType.getField(valueType, fieldName); - Code code = generator.newCode(); - Local<Instance> localInstance = code.newParameter(objectType); - Local<V> localNewValue = code.newParameter(valueType); + Code code = generatedType.getMethod(valueType, "call", objectType, valueType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Instance> localInstance = code.getParameter(0, objectType); + Local<V> localNewValue = code.getParameter(1, valueType); Local<V> localOldValue = code.newLocal(valueType); code.iget(field, localInstance, localOldValue); code.iput(field, localInstance, localNewValue); code.returnValue(localOldValue); - return newMethod(valueClass, code, Instance.class, valueClass); + return getMethod(); } public void testReadAndWriteStaticFields() throws Exception { @@ -961,7 +973,7 @@ public final class DexGeneratorTest extends TestCase { private <V> java.lang.reflect.Method newStaticSwapMethod(Class<V> valueClass, String fieldName) throws Exception { /* - * public static int generatedMethod(int newValue) { + * public static int call(int newValue) { * int oldValue = Static.intValue; * Static.intValue = newValue; * return oldValue; @@ -971,13 +983,58 @@ public final class DexGeneratorTest extends TestCase { Type<V> valueType = generator.getType(valueClass); Type<Static> objectType = generator.getType(Static.class); Field<Static, V> field = objectType.getField(valueType, fieldName); - Code code = generator.newCode(); - Local<V> localNewValue = code.newParameter(valueType); + Code code = generatedType.getMethod(valueType, "call", valueType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<V> localNewValue = code.getParameter(0, valueType); Local<V> localOldValue = code.newLocal(valueType); code.sget(field, localOldValue); code.sput(field, localNewValue); code.returnValue(localOldValue); - return newMethod(valueClass, code, valueClass); + return getMethod(); + } + + public void testTypeCast() throws Exception { + /* + * public static String call(Object o) { + * String s = (String) o; + * } + */ + Code code = generatedType.getMethod(stringType, "call", objectType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Object> localObject = code.getParameter(0, objectType); + Local<String> localString = code.newLocal(stringType); + code.typeCast(localObject, localString); + code.returnValue(localString); + + java.lang.reflect.Method method = getMethod(); + assertEquals("s", method.invoke(null, "s")); + assertEquals(null, method.invoke(null, (String) null)); + try { + method.invoke(null, 5); + fail(); + } catch (InvocationTargetException expected) { + assertEquals(ClassCastException.class, expected.getCause().getClass()); + } + } + + public void testInstanceOf() throws Exception { + /* + * public static boolean call(Object o) { + * boolean result = o instanceof String; + * return result; + * } + */ + Code code = generatedType.getMethod(booleanType, "call", objectType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Object> localObject = code.getParameter(0, objectType); + Local<Boolean> localResult = code.newLocal(booleanType); + code.instanceOfType(localResult, localObject, stringType); + code.returnValue(localResult); + + java.lang.reflect.Method method = getMethod(); + assertEquals(true, method.invoke(null, "s")); + assertEquals(false, method.invoke(null, (String) null)); + assertEquals(false, method.invoke(null, 5)); } /** @@ -985,7 +1042,7 @@ public final class DexGeneratorTest extends TestCase { */ public void testForLoop() throws Exception { /* - * public static int generatedMethod(int count) { + * public static int call(int count) { * int result = 1; * for (int i = 0; i < count; i += 1) { * result = result * 2; @@ -993,8 +1050,9 @@ public final class DexGeneratorTest extends TestCase { * return result; * } */ - Code code = generator.newCode(); - Local<Integer> localCount = code.newParameter(intType); + Code code = generatedType.getMethod(intType, "call", intType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Integer> localCount = code.getParameter(0, intType); Local<Integer> localResult = code.newLocal(intType); Local<Integer> localI = code.newLocal(intType); Local<Integer> local1 = code.newLocal(intType); @@ -1016,7 +1074,7 @@ public final class DexGeneratorTest extends TestCase { code.mark(afterLoop); code.returnValue(localResult); - java.lang.reflect.Method pow2 = newMethod(int.class, code, int.class); + java.lang.reflect.Method pow2 = getMethod(); assertEquals(1, pow2.invoke(null, 0)); assertEquals(2, pow2.invoke(null, 1)); assertEquals(4, pow2.invoke(null, 2)); @@ -1029,7 +1087,7 @@ public final class DexGeneratorTest extends TestCase { */ public void testWhileLoop() throws Exception { /* - * public static int generatedMethod(int max) { + * public static int call(int max) { * int result = 1; * while (result < max) { * result = result * 2; @@ -1037,8 +1095,9 @@ public final class DexGeneratorTest extends TestCase { * return result; * } */ - Code code = generator.newCode(); - Local<Integer> localMax = code.newParameter(intType); + Code code = generatedType.getMethod(intType, "call", intType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Integer> localMax = code.getParameter(0, intType); Local<Integer> localResult = code.newLocal(intType); Local<Integer> local2 = code.newLocal(intType); code.loadConstant(localResult, 1); @@ -1055,7 +1114,7 @@ public final class DexGeneratorTest extends TestCase { code.mark(afterLoop); code.returnValue(localResult); - java.lang.reflect.Method ceilPow2 = newMethod(int.class, code, int.class); + java.lang.reflect.Method ceilPow2 = getMethod(); assertEquals(1, ceilPow2.invoke(null, 1)); assertEquals(2, ceilPow2.invoke(null, 2)); assertEquals(4, ceilPow2.invoke(null, 3)); @@ -1066,7 +1125,7 @@ public final class DexGeneratorTest extends TestCase { public void testIfElseBlock() throws Exception { /* - * public static int generatedMethod(int a, int b, int c) { + * public static int call(int a, int b, int c) { * if (a < b) { * if (a < c) { * return a; @@ -1080,10 +1139,11 @@ public final class DexGeneratorTest extends TestCase { * } * } */ - Code code = generator.newCode(); - Local<Integer> localA = code.newParameter(intType); - Local<Integer> localB = code.newParameter(intType); - Local<Integer> localC = code.newParameter(intType); + Code code = generatedType.getMethod(intType, "call", intType, intType, intType) + .declare(ACC_PUBLIC | ACC_STATIC); + Local<Integer> localA = code.getParameter(0, intType); + Local<Integer> localB = code.getParameter(1, intType); + Local<Integer> localC = code.getParameter(2, intType); Label aLessThanB = code.newLabel(); Label aLessThanC = code.newLabel(); Label bLessThanC = code.newLabel(); @@ -1101,16 +1161,58 @@ public final class DexGeneratorTest extends TestCase { code.mark(bLessThanC); code.returnValue(localB); - java.lang.reflect.Method min = newMethod(int.class, code, int.class, int.class, int.class); + java.lang.reflect.Method min = getMethod(); assertEquals(1, min.invoke(null, 1, 2, 3)); assertEquals(1, min.invoke(null, 2, 3, 1)); assertEquals(1, min.invoke(null, 2, 1, 3)); assertEquals(1, min.invoke(null, 3, 2, 1)); } - // TODO: cast + public void testRecursion() throws Exception { - // TODO: instanceof + /* + * public static int call(int a) { + * if (a < 2) { + * return a; + * } + * a -= 1; + * int x = call(a) + * a -= 1; + * int y = call(a); + * int result = x + y; + * return result; + * } + */ + Method<?, Integer> c = generatedType.getMethod(intType, "call", intType); + Code code = c.declare(ACC_PUBLIC | ACC_STATIC); + Local<Integer> localA = code.getParameter(0, intType); + Local<Integer> local1 = code.newLocal(intType); + Local<Integer> local2 = code.newLocal(intType); + Local<Integer> localX = code.newLocal(intType); + Local<Integer> localY = code.newLocal(intType); + Local<Integer> localResult = code.newLocal(intType); + Label baseCase = code.newLabel(); + code.loadConstant(local1, 1); + code.loadConstant(local2, 2); + code.compare(Comparison.LT, localA, local2, baseCase); + code.op(BinaryOp.SUBTRACT, localA, localA, local1); + code.invokeStatic(c, localX, localA); + code.op(BinaryOp.SUBTRACT, localA, localA, local1); + code.invokeStatic(c, localY, localA); + code.op(BinaryOp.ADD, localResult, localX, localY); + code.returnValue(localResult); + code.mark(baseCase); + code.returnValue(localA); + + java.lang.reflect.Method fib = getMethod(); + assertEquals(0, fib.invoke(null, 0)); + assertEquals(1, fib.invoke(null, 1)); + assertEquals(1, fib.invoke(null, 2)); + assertEquals(2, fib.invoke(null, 3)); + assertEquals(3, fib.invoke(null, 4)); + assertEquals(5, fib.invoke(null, 5)); + assertEquals(8, fib.invoke(null, 6)); + } // TODO: array ops including new array, aget, etc. @@ -1121,37 +1223,26 @@ public final class DexGeneratorTest extends TestCase { // TODO: fail if a label is unreachable (never navigated to) private void addDefaultConstructor() { - Code code = generator.newCode(); - Local<?> thisRef = code.newThisLocal(generatedType); + Code code = generatedType.getConstructor().declare(ACC_PUBLIC | ACC_CONSTRUCTOR); + Local<?> thisRef = code.getThis(generatedType); code.invokeDirect(objectType.getConstructor(), null, thisRef); code.returnVoid(); - generatedType.getConstructor().declare(ACC_PUBLIC | ACC_CONSTRUCTOR, code); } /** - * Returns a method containing the body of {@code code}, accepting the - * {@code argClasses}, and returning {@code argClasses}. + * Returns the generated method. */ - private java.lang.reflect.Method newMethod( - Class<?> returnClass, Code code, Class<?>... argClasses) throws Exception { - Type<?> returnType = generator.getType(returnClass); - Type<?>[] argTypes = new Type<?>[argClasses.length]; - for (int i = 0; i < argClasses.length; i++) { - argTypes[i] = generator.getType(argClasses[i]); + private java.lang.reflect.Method getMethod() throws Exception { + Class<?> generated = loadAndGenerate(); + for (java.lang.reflect.Method method : generated.getMethods()) { + if (method.getName().equals("call")) { + return method; + } } - - Method<?, ?> method = generatedType.getMethod(returnType, "generatedMethod", argTypes); - generatedType.declare("Generated.java", ACC_PUBLIC, objectType); - method.declare(ACC_PUBLIC | ACC_STATIC, code); - return loadAndGenerate().getMethod("generatedMethod", argClasses); + throw new IllegalStateException("no call() method"); } private Class<?> loadAndGenerate() throws IOException, ClassNotFoundException { return generator.load(DexGeneratorTest.class.getClassLoader()).loadClass("Generated"); } - - private static Class<?> unbox(Class<?> boxed) { - Class<?> unboxed = BOXED_TO_PRIMITIVE.get(boxed); - return unboxed != null ? unboxed : boxed; - } } diff --git a/dx/src/com/android/dx/gen/BinaryOp.java b/dx/src/com/android/dx/gen/BinaryOp.java index 9c8f25e2b..65a29983a 100644 --- a/dx/src/com/android/dx/gen/BinaryOp.java +++ b/dx/src/com/android/dx/gen/BinaryOp.java @@ -21,59 +21,93 @@ import com.android.dx.rop.code.Rops; import com.android.dx.rop.type.TypeList; /** - * A binary operation on two values of the same type. + * An operation on two values of the same type. + * + * <p>Math operations ({@link #ADD}, {@link #SUBTRACT}, {@link #MULTIPLY}, + * {@link #DIVIDE}, and {@link #REMAINDER}) support ints, longs, floats and + * doubles. + * + * <p>Bit operations ({@link #AND}, {@link #OR}, {@link #XOR}, {@link + * #SHIFT_LEFT}, {@link #SHIFT_RIGHT}, {@link #UNSIGNED_SHIFT_RIGHT}) support + * ints and longs. + * + * <p>Division by zero behaves differently depending on the operand type. + * For int and long operands, {@link #DIVIDE} and {@link #REMAINDER} throw + * {@link ArithmeticException} if {@code b == 0}. For float and double operands, + * the operations return {@code NaN}. */ public enum BinaryOp { + /** {@code a + b} */ ADD() { @Override Rop rop(TypeList types) { return Rops.opAdd(types); } }, + + /** {@code a - b} */ SUBTRACT() { @Override Rop rop(TypeList types) { return Rops.opSub(types); } }, + + /** {@code a * b} */ MULTIPLY() { @Override Rop rop(TypeList types) { return Rops.opMul(types); } }, + + /** {@code a / b} */ DIVIDE() { @Override Rop rop(TypeList types) { return Rops.opDiv(types); } }, + + /** {@code a % b} */ REMAINDER() { @Override Rop rop(TypeList types) { return Rops.opRem(types); } }, + + /** {@code a & b} */ AND() { @Override Rop rop(TypeList types) { return Rops.opAnd(types); } }, + + /** {@code a | b} */ OR() { @Override Rop rop(TypeList types) { return Rops.opOr(types); } }, + + /** {@code a ^ b} */ XOR() { @Override Rop rop(TypeList types) { return Rops.opXor(types); } }, + + /** {@code a << b} */ SHIFT_LEFT() { @Override Rop rop(TypeList types) { return Rops.opShl(types); } }, + + /** {@code a >> b} */ SHIFT_RIGHT() { @Override Rop rop(TypeList types) { return Rops.opShr(types); } }, + + /** {@code a >>> b} */ UNSIGNED_SHIFT_RIGHT() { @Override Rop rop(TypeList types) { return Rops.opUshr(types); diff --git a/dx/src/com/android/dx/gen/Code.java b/dx/src/com/android/dx/gen/Code.java index c3c574e74..6ce9e2aa2 100644 --- a/dx/src/com/android/dx/gen/Code.java +++ b/dx/src/com/android/dx/gen/Code.java @@ -32,12 +32,9 @@ import com.android.dx.rop.code.ThrowingInsn; import com.android.dx.rop.type.StdTypeList; import static com.android.dx.rop.type.Type.BT_BYTE; import static com.android.dx.rop.type.Type.BT_CHAR; -import static com.android.dx.rop.type.Type.BT_DOUBLE; import static com.android.dx.rop.type.Type.BT_INT; -import static com.android.dx.rop.type.Type.BT_LONG; import static com.android.dx.rop.type.Type.BT_SHORT; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -45,6 +42,7 @@ import java.util.List; * Builds a sequence of instructions. */ public final class Code { + private final Method<?, ?> method; /** * All allocated labels. Although the order of the labels in this list * shouldn't impact behavior, it is used to determine basic block indices. @@ -59,26 +57,46 @@ public final class Code { /** true once we've fixed the positions of the parameter registers */ private boolean localsInitialized; + + private final Local<?> thisLocal; + private final List<Local<?>> parameters = new ArrayList<Local<?>>(); private final List<Local<?>> locals = new ArrayList<Local<?>>(); private SourcePosition sourcePosition = SourcePosition.NO_INFO; - Code(DexGenerator generator) { + public Code(Method<?, ?> method) { + this.method = method; + thisLocal = method.isStatic() + ? null + : new Local<Object>(this, method.declaringType, Local.InitialValue.THIS); + for (Type<?> parameter : method.parameters.types) { + parameters.add(new Local<Object>(this, parameter, Local.InitialValue.PARAMETER)); + } this.currentLabel = newLabel(); this.currentLabel.marked = true; } - // locals - public <T> Local<T> newLocal(Type<T> type) { return allocateLocal(type, Local.InitialValue.NONE); } - public <T> Local<T> newParameter(Type<T> type) { - return allocateLocal(type, Local.InitialValue.PARAMETER); + public <T> Local<T> getParameter(int index, Type<T> type) { + return coerce(parameters.get(index), type); } - public Local<?> newThisLocal(Type<?> type) { - return allocateLocal(type, Local.InitialValue.THIS); + public <T> Local<T> getThis(Type<T> type) { + if (thisLocal == null) { + throw new IllegalStateException("static methods cannot access 'this'"); + } + return coerce(thisLocal, type); + } + + @SuppressWarnings("unchecked") // guarded by an equals check + private <T> Local<T> coerce(Local<?> local, Type<T> expectedType) { + if (!local.type.equals(expectedType)) { + throw new IllegalArgumentException( + "requested " + expectedType + " but was " + local.type); + } + return (Local<T>) local; } private <T> Local<T> allocateLocal(Type<T> type, Local.InitialValue initialValue) { @@ -90,26 +108,28 @@ public final class Code { return result; } + /** + * Assigns registers to locals. From the spec: + * "the N arguments to a method land in the last N registers of the + * method's invocation frame, in order. Wide arguments consume two + * registers. Instance methods are passed a this reference as their + * first argument." + */ void initializeLocals() { if (localsInitialized) { throw new AssertionError(); } localsInitialized = true; - Collections.sort(locals, Local.ORDER_BY_INITIAL_VALUE_TYPE); int reg = 0; for (Local<?> local : locals) { - local.initialize(reg); - - switch (local.type.ropType.getBasicType()) { - case BT_LONG: - case BT_DOUBLE: - reg += 2; - break; - default: - reg += 1; - break; - } + reg += local.initialize(reg); + } + if (thisLocal != null) { + reg += thisLocal.initialize(reg); + } + for (Local<?> local : parameters) { + reg += local.initialize(reg); } } @@ -222,7 +242,7 @@ public final class Code { unary(Rops.opNot(source.type.ropType), source, target); } - public void cast(Local<?> source, Local<?> target) { + public void numericCast(Local<?> source, Local<?> target) { unary(getCastRop(source.type.ropType, target.type.ropType), source, target); } @@ -261,6 +281,10 @@ public final class Code { // instructions: branches + /** + * Compare integers. If the comparison is true, execution jumps to {@code + * trueLabel}. If it is false, execution continues to the next instruction. + */ public <T> void compare(Comparison comparison, Local<T> a, Local<T> b, Label trueLabel) { if (trueLabel == null) { throw new IllegalArgumentException(); @@ -312,32 +336,32 @@ public final class Code { invokeDirect(constructor, null, target, args); } - public <R> void invokeStatic(Method<?, R> method, Local<R> target, Local<?>... args) { + public <R> void invokeStatic(Method<?, R> method, Local<? super R> target, Local<?>... args) { invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args); } - public <I, R> void invokeVirtual(Method<I, R> method, Local<R> target, Local<I> object, - Local<?>... args) { + public <I, R> void invokeVirtual(Method<I, R> method, Local<? super R> target, + Local<? extends I> object, Local<?>... args) { invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, object, args); } - public <I, R> void invokeDirect(Method<?, R> method, Local<R> target, Local<I> object, - Local<?>... args) { + public <I, R> void invokeDirect(Method<I, R> method, Local<? super R> target, + Local<? extends I> object, Local<?>... args) { invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, object, args); } - public <I, R> void invokeSuper(Method<I, R> method, Local<R> target, Local<?> object, - Local<?>... args) { + public <I, R> void invokeSuper(Method<I, R> method, Local<? super R> target, + Local<? extends I> object, Local<?>... args) { invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, object, args); } - public <I, R> void invokeInterface(Method<I, R> method, Local<R> target, Local<?> object, - Local<?>... args) { + public <I, R> void invokeInterface(Method<I, R> method, Local<? super R> target, + Local<? extends I> object, Local<?>... args) { invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, object, args); } - private <I, R> void invoke(Rop rop, Method method, Local<R> target, Local<I> object, - Local<?>... args) { + private <I, R> void invoke(Rop rop, Method<I, R> method, Local<? super R> target, + Local<? extends I> object, Local<?>... args) { addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args), StdTypeList.EMPTY, method.constant)); if (target != null) { @@ -345,14 +369,37 @@ public final class Code { } } + // instructions: types + + public void instanceOfType(Local<?> target, Local<?> source, Type<?> type) { + addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition, + RegisterSpecList.make(source.spec()), StdTypeList.EMPTY, type.constant)); + moveResult(target, true); + } + + public void typeCast(Local<?> source, Local<?> target) { + addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition, + RegisterSpecList.make(source.spec()), StdTypeList.EMPTY, target.type.constant)); + moveResult(target, true); + } + // instructions: return public void returnVoid() { + if (!method.returnType.isVoid()) { + throw new IllegalArgumentException("declared " + method.returnType + + " but returned void"); + } addInstruction(new PlainInsn(Rops.RETURN_VOID, sourcePosition, null, RegisterSpecList.EMPTY)); } public void returnValue(Local<?> result) { + if (!result.type.equals(method.returnType)) { + // TODO: this is probably too strict. + throw new IllegalArgumentException("declared " + method.returnType + + " but returned " + result.type); + } addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition, null, RegisterSpecList.make(result.spec())), null); } @@ -392,25 +439,6 @@ public final class Code { } } - TypeList parameters() { - List<Type<?>> result = new ArrayList<Type<?>>(); - for (Local<?> local : locals) { - if (local.initialValue == Local.InitialValue.PARAMETER) { - result.add(local.type); - } - } - return new TypeList(result); - } - - Local<?> thisLocal() { - for (Local<?> local : locals) { - if (local.initialValue == Local.InitialValue.THIS) { - return local; - } - } - return null; - } - private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) { int offset = (first != null) ? 1 : 0; RegisterSpecList result = new RegisterSpecList(offset + rest.length); diff --git a/dx/src/com/android/dx/gen/DexGenerator.java b/dx/src/com/android/dx/gen/DexGenerator.java index 8a9f5a314..5355cec61 100644 --- a/dx/src/com/android/dx/gen/DexGenerator.java +++ b/dx/src/com/android/dx/gen/DexGenerator.java @@ -18,18 +18,6 @@ package com.android.dx.gen; import com.android.dx.dex.DexFormat; import com.android.dx.dex.file.DexFile; -import com.android.dx.rop.cst.CstBoolean; -import com.android.dx.rop.cst.CstByte; -import com.android.dx.rop.cst.CstChar; -import com.android.dx.rop.cst.CstDouble; -import com.android.dx.rop.cst.CstFloat; -import com.android.dx.rop.cst.CstInteger; -import com.android.dx.rop.cst.CstKnownNull; -import com.android.dx.rop.cst.CstLong; -import com.android.dx.rop.cst.CstShort; -import com.android.dx.rop.cst.CstString; -import com.android.dx.rop.cst.CstType; -import com.android.dx.rop.cst.TypedConstant; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -46,18 +34,14 @@ public final class DexGenerator { /** * @param name a descriptor like "(Ljava/lang/Class;[I)Ljava/lang/Object;". */ - public Type<?> getType(String name) { - return types.get(new Type<Object>(this, name)); + public <T> Type<T> getType(String name) { + return types.get(new Type<T>(this, name)); } public <T> Type<T> getType(Class<T> type) { return types.get(new Type<T>(this, type)); } - public Code newCode() { - return new Code(this); - } - /** * Returns a .dex formatted file. */ diff --git a/dx/src/com/android/dx/gen/Field.java b/dx/src/com/android/dx/gen/Field.java index 64f7b4c1e..f6a42cfe5 100644 --- a/dx/src/com/android/dx/gen/Field.java +++ b/dx/src/com/android/dx/gen/Field.java @@ -67,10 +67,10 @@ public final class Field<T, R> { */ public void declare(int accessFlags, Object staticValue) { if (declared) { - throw new IllegalStateException(); + throw new IllegalStateException("already declared: " + this); } if ((accessFlags & (AccessFlags.ACC_STATIC)) == 0 && staticValue != null) { - throw new IllegalArgumentException("Instance fields may not have a value"); + throw new IllegalArgumentException("instance fields may not have a value"); } this.declared = true; this.accessFlags = accessFlags; diff --git a/dx/src/com/android/dx/gen/Local.java b/dx/src/com/android/dx/gen/Local.java index 1c23011f8..db2d909b0 100644 --- a/dx/src/com/android/dx/gen/Local.java +++ b/dx/src/com/android/dx/gen/Local.java @@ -17,23 +17,13 @@ package com.android.dx.gen; import com.android.dx.rop.code.RegisterSpec; -import java.util.Comparator; +import static com.android.dx.rop.type.Type.BT_DOUBLE; +import static com.android.dx.rop.type.Type.BT_LONG; /** * A temporary variable that holds a single value. */ public final class Local<T> { - /** - * Dalvik bytecode uses the last N registers for the method's N arguments. - * Instance methods are passed 'this' as the first argument. This ordering - * sorts locals into this sequence. - */ - static final Comparator<Local<?>> ORDER_BY_INITIAL_VALUE_TYPE = new Comparator<Local<?>>() { - public int compare(Local<?> a, Local<?> b) { - return a.initialValue.ordinal() - b.initialValue.ordinal(); - } - }; - private final Code code; final Type type; final InitialValue initialValue; @@ -46,9 +36,22 @@ public final class Local<T> { this.initialValue = initialValue; } - void initialize(int reg) { + /** + * Assigns registers to this local. + * + * @return the number of registers required. + */ + int initialize(int reg) { this.reg = reg; this.spec = RegisterSpec.make(reg, type.getRopType()); + + switch (type.ropType.getBasicType()) { + case BT_LONG: + case BT_DOUBLE: + return 2; + default: + return 1; + } } RegisterSpec spec() { diff --git a/dx/src/com/android/dx/gen/Method.java b/dx/src/com/android/dx/gen/Method.java index e88a03145..343510a7f 100644 --- a/dx/src/com/android/dx/gen/Method.java +++ b/dx/src/com/android/dx/gen/Method.java @@ -100,31 +100,27 @@ public final class Method<T, R> { /** * @param accessFlags any flags masked by {@link AccessFlags#METHOD_FLAGS}. */ - public void declare(int accessFlags, Code code) { + public Code declare(int accessFlags) { if (declared) { - throw new IllegalStateException(); - } - if (code == null) { - throw new NullPointerException(); // TODO: permit methods without code - } - if (!parameters.equals(code.parameters())) { - throw new IllegalArgumentException("Parameters mismatch. Expected (" + parameters - + ") but was (" + code.parameters() + ")"); - } - boolean isStatic = (accessFlags & (ACC_STATIC)) != 0; - if (isStatic != (code.thisLocal() == null)) { - throw new IllegalArgumentException("Static mismatch. Declared static=" + isStatic - + " this local=" + code.thisLocal()); + throw new IllegalStateException("already declared: " + this); } this.declared = true; this.accessFlags = accessFlags; - this.code = code; + this.code = new Code(this); + return code; } boolean isDeclared() { return declared; } + boolean isStatic() { + if (!declared) { + throw new IllegalStateException(); + } + return (accessFlags & ACC_STATIC) != 0; + } + boolean isDirect() { if (!declared) { throw new IllegalStateException(); diff --git a/dx/src/com/android/dx/gen/Type.java b/dx/src/com/android/dx/gen/Type.java index 8cce3c62e..72e804f0c 100644 --- a/dx/src/com/android/dx/gen/Type.java +++ b/dx/src/com/android/dx/gen/Type.java @@ -105,7 +105,7 @@ public final class Type<T> { */ public void declare(String sourceFile, int flags, Type<?> supertype, Type<?>... interfaces) { if (declared) { - throw new IllegalStateException(); + throw new IllegalStateException("already declared: " + this); } this.declared = true; this.flags = flags; @@ -114,6 +114,10 @@ public final class Type<T> { this.interfaces = new TypeList(interfaces); } + boolean isVoid() { + return constant.getClassType() == com.android.dx.rop.type.Type.VOID; + } + boolean isDeclared() { return declared; } |