Support catch blocks.
Add support for throw calls. This is awkwardly named throwValue
because 'throw' is a keyword. Better names very welcome.
Add support for unused parameters.
Add support for float, double and long comparisons.
Add support for array operations.
Change-Id: I48380fca06c13175cd13b4e3613a4ba63902ed33
http://code.google.com/p/android/issues/detail?id=6322
diff --git a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java b/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java
index 8a9a669..22fb3da 100644
--- a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java
+++ b/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java
@@ -24,6 +24,7 @@
import static com.android.dx.rop.code.AccessFlags.ACC_STATIC;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import junit.framework.TestCase;
@@ -37,11 +38,19 @@
*/
public final class DexGeneratorTest extends TestCase {
private DexGenerator generator;
+ private Type<Void> voidType;
private Type<Integer> intType;
private Type<Long> longType;
private Type<Boolean> booleanType;
+ private Type<Float> floatType;
+ private Type<Double> doubleType;
private Type<Object> objectType;
private Type<String> stringType;
+ private Type<boolean[]> booleanArrayType;
+ private Type<int[]> intArrayType;
+ private Type<long[]> longArrayType;
+ private Type<long[][]> long2dArrayType;
+ private Type<Object[]> objectArrayType;
private Type<DexGeneratorTest> dexGeneratorTestType;
private Type<?> generatedType;
private Type<Callable> callableType;
@@ -58,11 +67,19 @@
*/
private void reset() {
generator = new DexGenerator();
+ voidType = generator.getType(void.class);
intType = generator.getType(int.class);
longType = generator.getType(long.class);
booleanType = generator.getType(boolean.class);
+ floatType = generator.getType(float.class);
+ doubleType = generator.getType(double.class);
objectType = generator.getType(Object.class);
stringType = generator.getType(String.class);
+ booleanArrayType = generator.getType(boolean[].class);
+ intArrayType = generator.getType(int[].class);
+ longArrayType = generator.getType(long[].class);
+ long2dArrayType = generator.getType(long[][].class);
+ objectArrayType = generator.getType(Object[].class);
dexGeneratorTestType = generator.getType(DexGeneratorTest.class);
generatedType = generator.getType("LGenerated;");
callableType = generator.getType(Callable.class);
@@ -218,7 +235,7 @@
}
@SuppressWarnings("unused") // called by generated code
- public final int superMethod(int a) {
+ public int superMethod(int a) {
return a + 4;
}
@@ -415,38 +432,38 @@
}
public void testBranching() throws Exception {
- java.lang.reflect.Method lt = newBranchingMethod(Comparison.LT);
+ java.lang.reflect.Method lt = branchingMethod(Comparison.LT);
assertEquals(Boolean.TRUE, lt.invoke(null, 1, 2));
assertEquals(Boolean.FALSE, lt.invoke(null, 1, 1));
assertEquals(Boolean.FALSE, lt.invoke(null, 2, 1));
- java.lang.reflect.Method le = newBranchingMethod(Comparison.LE);
+ java.lang.reflect.Method le = branchingMethod(Comparison.LE);
assertEquals(Boolean.TRUE, le.invoke(null, 1, 2));
assertEquals(Boolean.TRUE, le.invoke(null, 1, 1));
assertEquals(Boolean.FALSE, le.invoke(null, 2, 1));
- java.lang.reflect.Method eq = newBranchingMethod(Comparison.EQ);
+ java.lang.reflect.Method eq = branchingMethod(Comparison.EQ);
assertEquals(Boolean.FALSE, eq.invoke(null, 1, 2));
assertEquals(Boolean.TRUE, eq.invoke(null, 1, 1));
assertEquals(Boolean.FALSE, eq.invoke(null, 2, 1));
- java.lang.reflect.Method ge = newBranchingMethod(Comparison.GE);
+ java.lang.reflect.Method ge = branchingMethod(Comparison.GE);
assertEquals(Boolean.FALSE, ge.invoke(null, 1, 2));
assertEquals(Boolean.TRUE, ge.invoke(null, 1, 1));
assertEquals(Boolean.TRUE, ge.invoke(null, 2, 1));
- java.lang.reflect.Method gt = newBranchingMethod(Comparison.GT);
+ java.lang.reflect.Method gt = branchingMethod(Comparison.GT);
assertEquals(Boolean.FALSE, gt.invoke(null, 1, 2));
assertEquals(Boolean.FALSE, gt.invoke(null, 1, 1));
assertEquals(Boolean.TRUE, gt.invoke(null, 2, 1));
- java.lang.reflect.Method ne = newBranchingMethod(Comparison.NE);
+ java.lang.reflect.Method ne = branchingMethod(Comparison.NE);
assertEquals(Boolean.TRUE, ne.invoke(null, 1, 2));
assertEquals(Boolean.FALSE, ne.invoke(null, 1, 1));
assertEquals(Boolean.TRUE, ne.invoke(null, 2, 1));
}
- private java.lang.reflect.Method newBranchingMethod(Comparison comparison) throws Exception {
+ private java.lang.reflect.Method branchingMethod(Comparison comparison) throws Exception {
/*
* public static boolean call(int localA, int localB) {
* if (a comparison b) {
@@ -477,53 +494,53 @@
}
public void testCastIntegerToInteger() throws Exception {
- java.lang.reflect.Method intToLong = newNumericCastingMethod(int.class, long.class);
+ java.lang.reflect.Method intToLong = numericCastingMethod(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 = newNumericCastingMethod(long.class, int.class);
+ java.lang.reflect.Method longToInt = numericCastingMethod(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 = newNumericCastingMethod(int.class, short.class);
+ java.lang.reflect.Method intToShort = numericCastingMethod(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 = newNumericCastingMethod(int.class, char.class);
+ java.lang.reflect.Method intToChar = numericCastingMethod(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 = newNumericCastingMethod(int.class, byte.class);
+ java.lang.reflect.Method intToByte = numericCastingMethod(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 = newNumericCastingMethod(int.class, float.class);
+ java.lang.reflect.Method intToFloat = numericCastingMethod(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 = newNumericCastingMethod(int.class, double.class);
+ java.lang.reflect.Method intToDouble = numericCastingMethod(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 = newNumericCastingMethod(long.class, float.class);
+ java.lang.reflect.Method longToFloat = numericCastingMethod(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 = newNumericCastingMethod(long.class, double.class);
+ java.lang.reflect.Method longToDouble = numericCastingMethod(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));
@@ -531,7 +548,7 @@
}
public void testCastFloatingPointToInteger() throws Exception {
- java.lang.reflect.Method floatToInt = newNumericCastingMethod(float.class, int.class);
+ java.lang.reflect.Method floatToInt = numericCastingMethod(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));
@@ -539,7 +556,7 @@
assertEquals(Integer.MIN_VALUE, floatToInt.invoke(null, Float.NEGATIVE_INFINITY));
assertEquals(0, floatToInt.invoke(null, Float.NaN));
- java.lang.reflect.Method floatToLong = newNumericCastingMethod(float.class, long.class);
+ java.lang.reflect.Method floatToLong = numericCastingMethod(float.class, long.class);
assertEquals(0L, floatToLong.invoke(null, 0.0f));
assertEquals(-1L, floatToLong.invoke(null, -1.0f));
assertEquals(10000000272564224L, floatToLong.invoke(null, 10e15f));
@@ -547,7 +564,7 @@
assertEquals(Long.MIN_VALUE, floatToLong.invoke(null, Float.NEGATIVE_INFINITY));
assertEquals(0L, floatToLong.invoke(null, Float.NaN));
- java.lang.reflect.Method doubleToInt = newNumericCastingMethod(double.class, int.class);
+ java.lang.reflect.Method doubleToInt = numericCastingMethod(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));
@@ -555,7 +572,7 @@
assertEquals(Integer.MIN_VALUE, doubleToInt.invoke(null, Double.NEGATIVE_INFINITY));
assertEquals(0, doubleToInt.invoke(null, Double.NaN));
- java.lang.reflect.Method doubleToLong = newNumericCastingMethod(double.class, long.class);
+ java.lang.reflect.Method doubleToLong = numericCastingMethod(double.class, long.class);
assertEquals(0L, doubleToLong.invoke(null, 0.0));
assertEquals(-1L, doubleToLong.invoke(null, -1.0));
assertEquals(10000000000000000L, doubleToLong.invoke(null, 10e15));
@@ -565,14 +582,14 @@
}
public void testCastFloatingPointToFloatingPoint() throws Exception {
- java.lang.reflect.Method floatToDouble = newNumericCastingMethod(float.class, double.class);
+ java.lang.reflect.Method floatToDouble = numericCastingMethod(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 = newNumericCastingMethod(double.class, float.class);
+ java.lang.reflect.Method doubleToFloat = numericCastingMethod(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));
@@ -580,7 +597,7 @@
assertEquals(Float.NaN, doubleToFloat.invoke(null, Double.NaN));
}
- private java.lang.reflect.Method newNumericCastingMethod(Class<?> source, Class<?> target)
+ private java.lang.reflect.Method numericCastingMethod(Class<?> source, Class<?> target)
throws Exception {
/*
* public static short call(int source) {
@@ -601,18 +618,18 @@
}
public void testNot() throws Exception {
- java.lang.reflect.Method notInteger = newNotMethod(int.class);
+ java.lang.reflect.Method notInteger = notMethod(int.class);
assertEquals(0xffffffff, notInteger.invoke(null, 0x00000000));
assertEquals(0x00000000, notInteger.invoke(null, 0xffffffff));
assertEquals(0xedcba987, notInteger.invoke(null, 0x12345678));
- java.lang.reflect.Method notLong = newNotMethod(long.class);
+ java.lang.reflect.Method notLong = notMethod(long.class);
assertEquals(0xffffffffffffffffL, notLong.invoke(null, 0x0000000000000000L));
assertEquals(0x0000000000000000L, notLong.invoke(null, 0xffffffffffffffffL));
assertEquals(0x98765432edcba987L, notLong.invoke(null, 0x6789abcd12345678L));
}
- private <T> java.lang.reflect.Method newNotMethod(Class<T> source) throws Exception {
+ private <T> java.lang.reflect.Method notMethod(Class<T> source) throws Exception {
/*
* public static short call(int source) {
* source = ~source;
@@ -630,30 +647,30 @@
}
public void testNegate() throws Exception {
- java.lang.reflect.Method negateInteger = newNegateMethod(int.class);
+ java.lang.reflect.Method negateInteger = negateMethod(int.class);
assertEquals(0, negateInteger.invoke(null, 0));
assertEquals(-1, negateInteger.invoke(null, 1));
assertEquals(Integer.MIN_VALUE, negateInteger.invoke(null, Integer.MIN_VALUE));
- java.lang.reflect.Method negateLong = newNegateMethod(long.class);
+ java.lang.reflect.Method negateLong = negateMethod(long.class);
assertEquals(0L, negateLong.invoke(null, 0));
assertEquals(-1L, negateLong.invoke(null, 1));
assertEquals(Long.MIN_VALUE, negateLong.invoke(null, Long.MIN_VALUE));
- java.lang.reflect.Method negateFloat = newNegateMethod(float.class);
+ java.lang.reflect.Method negateFloat = negateMethod(float.class);
assertEquals(-0.0f, negateFloat.invoke(null, 0.0f));
assertEquals(-1.0f, negateFloat.invoke(null, 1.0f));
assertEquals(Float.NaN, negateFloat.invoke(null, Float.NaN));
assertEquals(Float.POSITIVE_INFINITY, negateFloat.invoke(null, Float.NEGATIVE_INFINITY));
- java.lang.reflect.Method negateDouble = newNegateMethod(double.class);
+ java.lang.reflect.Method negateDouble = negateMethod(double.class);
assertEquals(-0.0, negateDouble.invoke(null, 0.0));
assertEquals(-1.0, negateDouble.invoke(null, 1.0));
assertEquals(Double.NaN, negateDouble.invoke(null, Double.NaN));
assertEquals(Double.POSITIVE_INFINITY, negateDouble.invoke(null, Double.NEGATIVE_INFINITY));
}
- private <T> java.lang.reflect.Method newNegateMethod(Class<T> source) throws Exception {
+ private <T> java.lang.reflect.Method negateMethod(Class<T> source) throws Exception {
/*
* public static short call(int source) {
* source = -source;
@@ -671,16 +688,16 @@
}
public void testIntBinaryOps() throws Exception {
- java.lang.reflect.Method add = newBinaryOpMethod(int.class, BinaryOp.ADD);
+ java.lang.reflect.Method add = binaryOpMethod(int.class, BinaryOp.ADD);
assertEquals(79, add.invoke(null, 75, 4));
- java.lang.reflect.Method subtract = newBinaryOpMethod(int.class, BinaryOp.SUBTRACT);
+ java.lang.reflect.Method subtract = binaryOpMethod(int.class, BinaryOp.SUBTRACT);
assertEquals(71, subtract.invoke(null, 75, 4));
- java.lang.reflect.Method multiply = newBinaryOpMethod(int.class, BinaryOp.MULTIPLY);
+ java.lang.reflect.Method multiply = binaryOpMethod(int.class, BinaryOp.MULTIPLY);
assertEquals(300, multiply.invoke(null, 75, 4));
- java.lang.reflect.Method divide = newBinaryOpMethod(int.class, BinaryOp.DIVIDE);
+ java.lang.reflect.Method divide = binaryOpMethod(int.class, BinaryOp.DIVIDE);
assertEquals(18, divide.invoke(null, 75, 4));
try {
divide.invoke(null, 75, 0);
@@ -689,7 +706,7 @@
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
- java.lang.reflect.Method remainder = newBinaryOpMethod(int.class, BinaryOp.REMAINDER);
+ java.lang.reflect.Method remainder = binaryOpMethod(int.class, BinaryOp.REMAINDER);
assertEquals(3, remainder.invoke(null, 75, 4));
try {
remainder.invoke(null, 75, 0);
@@ -698,37 +715,37 @@
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
- java.lang.reflect.Method and = newBinaryOpMethod(int.class, BinaryOp.AND);
+ java.lang.reflect.Method and = binaryOpMethod(int.class, BinaryOp.AND);
assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000));
- java.lang.reflect.Method or = newBinaryOpMethod(int.class, BinaryOp.OR);
+ java.lang.reflect.Method or = binaryOpMethod(int.class, BinaryOp.OR);
assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000));
- java.lang.reflect.Method xor = newBinaryOpMethod(int.class, BinaryOp.XOR);
+ java.lang.reflect.Method xor = binaryOpMethod(int.class, BinaryOp.XOR);
assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000));
- java.lang.reflect.Method shiftLeft = newBinaryOpMethod(int.class, BinaryOp.SHIFT_LEFT);
+ java.lang.reflect.Method shiftLeft = binaryOpMethod(int.class, BinaryOp.SHIFT_LEFT);
assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8));
- java.lang.reflect.Method shiftRight = newBinaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT);
+ java.lang.reflect.Method shiftRight = binaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT);
assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8));
- java.lang.reflect.Method unsignedShiftRight = newBinaryOpMethod(int.class,
+ java.lang.reflect.Method unsignedShiftRight = binaryOpMethod(int.class,
BinaryOp.UNSIGNED_SHIFT_RIGHT);
assertEquals(0x00abcd12, unsignedShiftRight.invoke(null, 0xabcd1234, 8));
}
public void testLongBinaryOps() throws Exception {
- java.lang.reflect.Method add = newBinaryOpMethod(long.class, BinaryOp.ADD);
+ java.lang.reflect.Method add = binaryOpMethod(long.class, BinaryOp.ADD);
assertEquals(79L, add.invoke(null, 75L, 4L));
- java.lang.reflect.Method subtract = newBinaryOpMethod(long.class, BinaryOp.SUBTRACT);
+ java.lang.reflect.Method subtract = binaryOpMethod(long.class, BinaryOp.SUBTRACT);
assertEquals(71L, subtract.invoke(null, 75L, 4L));
- java.lang.reflect.Method multiply = newBinaryOpMethod(long.class, BinaryOp.MULTIPLY);
+ java.lang.reflect.Method multiply = binaryOpMethod(long.class, BinaryOp.MULTIPLY);
assertEquals(300L, multiply.invoke(null, 75L, 4L));
- java.lang.reflect.Method divide = newBinaryOpMethod(long.class, BinaryOp.DIVIDE);
+ java.lang.reflect.Method divide = binaryOpMethod(long.class, BinaryOp.DIVIDE);
assertEquals(18L, divide.invoke(null, 75L, 4L));
try {
divide.invoke(null, 75L, 0L);
@@ -737,7 +754,7 @@
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
- java.lang.reflect.Method remainder = newBinaryOpMethod(long.class, BinaryOp.REMAINDER);
+ java.lang.reflect.Method remainder = binaryOpMethod(long.class, BinaryOp.REMAINDER);
assertEquals(3L, remainder.invoke(null, 75L, 4L));
try {
remainder.invoke(null, 75L, 0L);
@@ -746,67 +763,67 @@
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
- java.lang.reflect.Method and = newBinaryOpMethod(long.class, BinaryOp.AND);
+ java.lang.reflect.Method and = binaryOpMethod(long.class, BinaryOp.AND);
assertEquals(0xff00ff0000000000L,
and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
- java.lang.reflect.Method or = newBinaryOpMethod(long.class, BinaryOp.OR);
+ java.lang.reflect.Method or = binaryOpMethod(long.class, BinaryOp.OR);
assertEquals(0xffffffffff00ff00L, or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
- java.lang.reflect.Method xor = newBinaryOpMethod(long.class, BinaryOp.XOR);
+ java.lang.reflect.Method xor = binaryOpMethod(long.class, BinaryOp.XOR);
assertEquals(0x00ff00ffff00ff00L,
xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
- java.lang.reflect.Method shiftLeft = newBinaryOpMethod(long.class, BinaryOp.SHIFT_LEFT);
+ java.lang.reflect.Method shiftLeft = binaryOpMethod(long.class, BinaryOp.SHIFT_LEFT);
assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8L));
- java.lang.reflect.Method shiftRight = newBinaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT);
+ java.lang.reflect.Method shiftRight = binaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT);
assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8L));
- java.lang.reflect.Method unsignedShiftRight = newBinaryOpMethod(long.class,
+ java.lang.reflect.Method unsignedShiftRight = binaryOpMethod(long.class,
BinaryOp.UNSIGNED_SHIFT_RIGHT);
assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8L));
}
public void testFloatBinaryOps() throws Exception {
- java.lang.reflect.Method add = newBinaryOpMethod(float.class, BinaryOp.ADD);
+ java.lang.reflect.Method add = binaryOpMethod(float.class, BinaryOp.ADD);
assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f));
- java.lang.reflect.Method subtract = newBinaryOpMethod(float.class, BinaryOp.SUBTRACT);
+ java.lang.reflect.Method subtract = binaryOpMethod(float.class, BinaryOp.SUBTRACT);
assertEquals(4.25f, subtract.invoke(null, 5.5f, 1.25f));
- java.lang.reflect.Method multiply = newBinaryOpMethod(float.class, BinaryOp.MULTIPLY);
+ java.lang.reflect.Method multiply = binaryOpMethod(float.class, BinaryOp.MULTIPLY);
assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f));
- java.lang.reflect.Method divide = newBinaryOpMethod(float.class, BinaryOp.DIVIDE);
+ java.lang.reflect.Method divide = binaryOpMethod(float.class, BinaryOp.DIVIDE);
assertEquals(4.4f, divide.invoke(null, 5.5f, 1.25f));
assertEquals(Float.POSITIVE_INFINITY, divide.invoke(null, 5.5f, 0.0f));
- java.lang.reflect.Method remainder = newBinaryOpMethod(float.class, BinaryOp.REMAINDER);
+ java.lang.reflect.Method remainder = binaryOpMethod(float.class, BinaryOp.REMAINDER);
assertEquals(0.5f, remainder.invoke(null, 5.5f, 1.25f));
assertEquals(Float.NaN, remainder.invoke(null, 5.5f, 0.0f));
}
public void testDoubleBinaryOps() throws Exception {
- java.lang.reflect.Method add = newBinaryOpMethod(double.class, BinaryOp.ADD);
+ java.lang.reflect.Method add = binaryOpMethod(double.class, BinaryOp.ADD);
assertEquals(6.75, add.invoke(null, 5.5, 1.25));
- java.lang.reflect.Method subtract = newBinaryOpMethod(double.class, BinaryOp.SUBTRACT);
+ java.lang.reflect.Method subtract = binaryOpMethod(double.class, BinaryOp.SUBTRACT);
assertEquals(4.25, subtract.invoke(null, 5.5, 1.25));
- java.lang.reflect.Method multiply = newBinaryOpMethod(double.class, BinaryOp.MULTIPLY);
+ java.lang.reflect.Method multiply = binaryOpMethod(double.class, BinaryOp.MULTIPLY);
assertEquals(6.875, multiply.invoke(null, 5.5, 1.25));
- java.lang.reflect.Method divide = newBinaryOpMethod(double.class, BinaryOp.DIVIDE);
+ java.lang.reflect.Method divide = binaryOpMethod(double.class, BinaryOp.DIVIDE);
assertEquals(4.4, divide.invoke(null, 5.5, 1.25));
assertEquals(Double.POSITIVE_INFINITY, divide.invoke(null, 5.5, 0.0));
- java.lang.reflect.Method remainder = newBinaryOpMethod(double.class, BinaryOp.REMAINDER);
+ java.lang.reflect.Method remainder = binaryOpMethod(double.class, BinaryOp.REMAINDER);
assertEquals(0.5, remainder.invoke(null, 5.5, 1.25));
assertEquals(Double.NaN, remainder.invoke(null, 5.5, 0.0));
}
- private <T> java.lang.reflect.Method newBinaryOpMethod(Class<T> valueClass, BinaryOp op)
+ private <T> java.lang.reflect.Method binaryOpMethod(Class<T> valueClass, BinaryOp op)
throws Exception {
/*
* public static int binaryOp(int a, int b) {
@@ -829,47 +846,47 @@
public void testReadAndWriteInstanceFields() throws Exception {
Instance instance = new Instance();
- java.lang.reflect.Method intSwap = newInstanceSwapMethod(int.class, "intValue");
+ java.lang.reflect.Method intSwap = instanceSwapMethod(int.class, "intValue");
instance.intValue = 5;
assertEquals(5, intSwap.invoke(null, instance, 10));
assertEquals(10, instance.intValue);
- java.lang.reflect.Method longSwap = newInstanceSwapMethod(long.class, "longValue");
+ java.lang.reflect.Method longSwap = instanceSwapMethod(long.class, "longValue");
instance.longValue = 500L;
assertEquals(500L, longSwap.invoke(null, instance, 1234L));
assertEquals(1234L, instance.longValue);
- java.lang.reflect.Method booleanSwap = newInstanceSwapMethod(boolean.class, "booleanValue");
+ java.lang.reflect.Method booleanSwap = instanceSwapMethod(boolean.class, "booleanValue");
instance.booleanValue = false;
assertEquals(false, booleanSwap.invoke(null, instance, true));
assertEquals(true, instance.booleanValue);
- java.lang.reflect.Method floatSwap = newInstanceSwapMethod(float.class, "floatValue");
+ java.lang.reflect.Method floatSwap = instanceSwapMethod(float.class, "floatValue");
instance.floatValue = 1.5f;
assertEquals(1.5f, floatSwap.invoke(null, instance, 0.5f));
assertEquals(0.5f, instance.floatValue);
- java.lang.reflect.Method doubleSwap = newInstanceSwapMethod(double.class, "doubleValue");
+ java.lang.reflect.Method doubleSwap = instanceSwapMethod(double.class, "doubleValue");
instance.doubleValue = 155.5;
assertEquals(155.5, doubleSwap.invoke(null, instance, 266.6));
assertEquals(266.6, instance.doubleValue);
- java.lang.reflect.Method objectSwap = newInstanceSwapMethod(Object.class, "objectValue");
+ java.lang.reflect.Method objectSwap = instanceSwapMethod(Object.class, "objectValue");
instance.objectValue = "before";
assertEquals("before", objectSwap.invoke(null, instance, "after"));
assertEquals("after", instance.objectValue);
- java.lang.reflect.Method byteSwap = newInstanceSwapMethod(byte.class, "byteValue");
+ java.lang.reflect.Method byteSwap = instanceSwapMethod(byte.class, "byteValue");
instance.byteValue = 0x35;
assertEquals((byte) 0x35, byteSwap.invoke(null, instance, (byte) 0x64));
assertEquals((byte) 0x64, instance.byteValue);
- java.lang.reflect.Method charSwap = newInstanceSwapMethod(char.class, "charValue");
+ java.lang.reflect.Method charSwap = instanceSwapMethod(char.class, "charValue");
instance.charValue = 'A';
assertEquals('A', charSwap.invoke(null, instance, 'B'));
assertEquals('B', instance.charValue);
- java.lang.reflect.Method shortSwap = newInstanceSwapMethod(short.class, "shortValue");
+ java.lang.reflect.Method shortSwap = instanceSwapMethod(short.class, "shortValue");
instance.shortValue = (short) 0xabcd;
assertEquals((short) 0xabcd, shortSwap.invoke(null, instance, (short) 0x1234));
assertEquals((short) 0x1234, instance.shortValue);
@@ -887,7 +904,7 @@
public short shortValue;
}
- private <V> java.lang.reflect.Method newInstanceSwapMethod(
+ private <V> java.lang.reflect.Method instanceSwapMethod(
Class<V> valueClass, String fieldName) throws Exception {
/*
* public static int call(Instance instance, int newValue) {
@@ -912,47 +929,47 @@
}
public void testReadAndWriteStaticFields() throws Exception {
- java.lang.reflect.Method intSwap = newStaticSwapMethod(int.class, "intValue");
+ java.lang.reflect.Method intSwap = staticSwapMethod(int.class, "intValue");
Static.intValue = 5;
assertEquals(5, intSwap.invoke(null, 10));
assertEquals(10, Static.intValue);
- java.lang.reflect.Method longSwap = newStaticSwapMethod(long.class, "longValue");
+ java.lang.reflect.Method longSwap = staticSwapMethod(long.class, "longValue");
Static.longValue = 500L;
assertEquals(500L, longSwap.invoke(null, 1234L));
assertEquals(1234L, Static.longValue);
- java.lang.reflect.Method booleanSwap = newStaticSwapMethod(boolean.class, "booleanValue");
+ java.lang.reflect.Method booleanSwap = staticSwapMethod(boolean.class, "booleanValue");
Static.booleanValue = false;
assertEquals(false, booleanSwap.invoke(null, true));
assertEquals(true, Static.booleanValue);
- java.lang.reflect.Method floatSwap = newStaticSwapMethod(float.class, "floatValue");
+ java.lang.reflect.Method floatSwap = staticSwapMethod(float.class, "floatValue");
Static.floatValue = 1.5f;
assertEquals(1.5f, floatSwap.invoke(null, 0.5f));
assertEquals(0.5f, Static.floatValue);
- java.lang.reflect.Method doubleSwap = newStaticSwapMethod(double.class, "doubleValue");
+ java.lang.reflect.Method doubleSwap = staticSwapMethod(double.class, "doubleValue");
Static.doubleValue = 155.5;
assertEquals(155.5, doubleSwap.invoke(null, 266.6));
assertEquals(266.6, Static.doubleValue);
- java.lang.reflect.Method objectSwap = newStaticSwapMethod(Object.class, "objectValue");
+ java.lang.reflect.Method objectSwap = staticSwapMethod(Object.class, "objectValue");
Static.objectValue = "before";
assertEquals("before", objectSwap.invoke(null, "after"));
assertEquals("after", Static.objectValue);
- java.lang.reflect.Method byteSwap = newStaticSwapMethod(byte.class, "byteValue");
+ java.lang.reflect.Method byteSwap = staticSwapMethod(byte.class, "byteValue");
Static.byteValue = 0x35;
assertEquals((byte) 0x35, byteSwap.invoke(null, (byte) 0x64));
assertEquals((byte) 0x64, Static.byteValue);
- java.lang.reflect.Method charSwap = newStaticSwapMethod(char.class, "charValue");
+ java.lang.reflect.Method charSwap = staticSwapMethod(char.class, "charValue");
Static.charValue = 'A';
assertEquals('A', charSwap.invoke(null, 'B'));
assertEquals('B', Static.charValue);
- java.lang.reflect.Method shortSwap = newStaticSwapMethod(short.class, "shortValue");
+ java.lang.reflect.Method shortSwap = staticSwapMethod(short.class, "shortValue");
Static.shortValue = (short) 0xabcd;
assertEquals((short) 0xabcd, shortSwap.invoke(null, (short) 0x1234));
assertEquals((short) 0x1234, Static.shortValue);
@@ -970,7 +987,7 @@
public static short shortValue;
}
- private <V> java.lang.reflect.Method newStaticSwapMethod(Class<V> valueClass, String fieldName)
+ private <V> java.lang.reflect.Method staticSwapMethod(Class<V> valueClass, String fieldName)
throws Exception {
/*
* public static int call(int newValue) {
@@ -1169,7 +1186,6 @@
}
public void testRecursion() throws Exception {
-
/*
* public static int call(int a) {
* if (a < 2) {
@@ -1214,14 +1230,391 @@
assertEquals(8, fib.invoke(null, 6));
}
- // TODO: array ops including new array, aget, etc.
+ public void testCatchExceptions() throws Exception {
+ /*
+ * public static String call(int i) {
+ * try {
+ * DexGeneratorTest.thrower(i);
+ * return "NONE";
+ * } catch (IllegalArgumentException e) {
+ * return "IAE";
+ * } catch (IllegalStateException e) {
+ * return "ISE";
+ * } catch (RuntimeException e) {
+ * return "RE";
+ * }
+ */
+ Code code = generatedType.getMethod(stringType, "call", intType)
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ Local<Integer> localI = code.getParameter(0, intType);
+ Local<String> result = code.newLocal(stringType);
+ Label catchIae = code.newLabel();
+ Label catchIse = code.newLabel();
+ Label catchRe = code.newLabel();
- // TODO: throw + catch
+ code.addCatchClause(generator.getType(IllegalArgumentException.class), catchIae);
+ code.addCatchClause(generator.getType(IllegalStateException.class), catchIse);
+ code.addCatchClause(generator.getType(RuntimeException.class), catchRe);
+ Method<?, ?> thrower = dexGeneratorTestType.getMethod(voidType, "thrower", intType);
+ code.invokeStatic(thrower, null, localI);
+ code.loadConstant(result, "NONE");
+ code.returnValue(result);
- // TODO: cmp float
+ code.mark(catchIae);
+ code.loadConstant(result, "IAE");
+ code.returnValue(result);
+
+ code.mark(catchIse);
+ code.loadConstant(result, "ISE");
+ code.returnValue(result);
+
+ code.mark(catchRe);
+ code.loadConstant(result, "RE");
+ code.returnValue(result);
+
+ java.lang.reflect.Method method = getMethod();
+ assertEquals("NONE", method.invoke(null, 0));
+ assertEquals("IAE", method.invoke(null, 1));
+ assertEquals("ISE", method.invoke(null, 2));
+ assertEquals("RE", method.invoke(null, 3));
+ try {
+ method.invoke(null, 4);
+ fail();
+ } catch (InvocationTargetException expected) {
+ assertEquals(IOException.class, expected.getCause().getClass());
+ }
+ }
+
+ @SuppressWarnings("unused") // called by generated code
+ public static void thrower(int a) throws Exception {
+ switch (a) {
+ case 0:
+ return;
+ case 1:
+ throw new IllegalArgumentException();
+ case 2:
+ throw new IllegalStateException();
+ case 3:
+ throw new UnsupportedOperationException();
+ case 4:
+ throw new IOException();
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ public void testNestedCatchClauses() throws Exception {
+ /*
+ * public static String call(int a, int b, int c) {
+ * try {
+ * DexGeneratorTest.thrower(a);
+ * try {
+ * DexGeneratorTest.thrower(b);
+ * } catch (IllegalArgumentException) {
+ * return "INNER";
+ * }
+ * DexGeneratorTest.thrower(c);
+ * return "NONE";
+ * } catch (IllegalArgumentException e) {
+ * return "OUTER";
+ * }
+ */
+ Code code = generatedType.getMethod(stringType, "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);
+ Local<String> localResult = code.newLocal(stringType);
+ Label catchInner = code.newLabel();
+ Label catchOuter = code.newLabel();
+
+ Type<IllegalArgumentException> iaeType = generator.getType(IllegalArgumentException.class);
+ code.addCatchClause(iaeType, catchOuter);
+
+ Method<?, ?> thrower = dexGeneratorTestType.getMethod(voidType, "thrower", intType);
+ code.invokeStatic(thrower, null, localA);
+
+ // for the inner catch clause, we stash the old label and put it back afterwards.
+ Label previousLabel = code.removeCatchClause(iaeType);
+ code.addCatchClause(iaeType, catchInner);
+ code.invokeStatic(thrower, null, localB);
+ code.removeCatchClause(iaeType);
+ code.addCatchClause(iaeType, previousLabel);
+ code.invokeStatic(thrower, null, localC);
+ code.loadConstant(localResult, "NONE");
+ code.returnValue(localResult);
+
+ code.mark(catchInner);
+ code.loadConstant(localResult, "INNER");
+ code.returnValue(localResult);
+
+ code.mark(catchOuter);
+ code.loadConstant(localResult, "OUTER");
+ code.returnValue(localResult);
+
+ java.lang.reflect.Method method = getMethod();
+ assertEquals("OUTER", method.invoke(null, 1, 0, 0));
+ assertEquals("INNER", method.invoke(null, 0, 1, 0));
+ assertEquals("OUTER", method.invoke(null, 0, 0, 1));
+ assertEquals("NONE", method.invoke(null, 0, 0, 0));
+ }
+
+ public void testThrow() throws Exception {
+ /*
+ * public static void call() {
+ * throw new IllegalStateException();
+ * }
+ */
+ Code code = generatedType.getMethod(voidType, "call")
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ Type<IllegalStateException> iseType = generator.getType(IllegalStateException.class);
+ Method<IllegalStateException, Void> iseConstructor = iseType.getConstructor();
+ Local<IllegalStateException> localIse = code.newLocal(iseType);
+ code.newInstance(localIse, iseConstructor);
+ code.throwValue(localIse);
+
+ try {
+ getMethod().invoke(null);
+ fail();
+ } catch (InvocationTargetException expected) {
+ assertEquals(IllegalStateException.class, expected.getCause().getClass());
+ }
+ }
+
+ public void testUnusedParameters() throws Exception {
+ /*
+ * public static void call(int unused1, long unused2, long unused3) {}
+ */
+ Code code = generatedType.getMethod(voidType, "call", intType, longType, longType)
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ code.returnVoid();
+ getMethod().invoke(null, 1, 2, 3);
+ }
+
+ public void testFloatingPointCompare() throws Exception {
+ java.lang.reflect.Method floatG = floatingPointCompareMethod(floatType, 1);
+ assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY));
+ assertEquals(-1, floatG.invoke(null, 1.0f, 2.0f));
+ assertEquals(0, floatG.invoke(null, 1.0f, 1.0f));
+ assertEquals(1, floatG.invoke(null, 2.0f, 1.0f));
+ assertEquals(1, floatG.invoke(null, 1.0f, Float.NaN));
+ assertEquals(1, floatG.invoke(null, Float.NaN, 1.0f));
+ assertEquals(1, floatG.invoke(null, Float.NaN, Float.NaN));
+ assertEquals(1, floatG.invoke(null, Float.NaN, Float.POSITIVE_INFINITY));
+
+ java.lang.reflect.Method floatL = floatingPointCompareMethod(floatType, -1);
+ assertEquals(-1, floatG.invoke(null, 1.0f, Float.POSITIVE_INFINITY));
+ assertEquals(-1, floatL.invoke(null, 1.0f, 2.0f));
+ assertEquals(0, floatL.invoke(null, 1.0f, 1.0f));
+ assertEquals(1, floatL.invoke(null, 2.0f, 1.0f));
+ assertEquals(-1, floatL.invoke(null, 1.0f, Float.NaN));
+ assertEquals(-1, floatL.invoke(null, Float.NaN, 1.0f));
+ assertEquals(-1, floatL.invoke(null, Float.NaN, Float.NaN));
+ assertEquals(-1, floatL.invoke(null, Float.NaN, Float.POSITIVE_INFINITY));
+
+ java.lang.reflect.Method doubleG = floatingPointCompareMethod(doubleType, 1);
+ assertEquals(-1, doubleG.invoke(null, 1.0, Double.POSITIVE_INFINITY));
+ assertEquals(-1, doubleG.invoke(null, 1.0, 2.0));
+ assertEquals(0, doubleG.invoke(null, 1.0, 1.0));
+ assertEquals(1, doubleG.invoke(null, 2.0, 1.0));
+ assertEquals(1, doubleG.invoke(null, 1.0, Double.NaN));
+ assertEquals(1, doubleG.invoke(null, Double.NaN, 1.0));
+ assertEquals(1, doubleG.invoke(null, Double.NaN, Double.NaN));
+ assertEquals(1, doubleG.invoke(null, Double.NaN, Double.POSITIVE_INFINITY));
+
+ java.lang.reflect.Method doubleL = floatingPointCompareMethod(doubleType, -1);
+ assertEquals(-1, doubleL.invoke(null, 1.0, Double.POSITIVE_INFINITY));
+ assertEquals(-1, doubleL.invoke(null, 1.0, 2.0));
+ assertEquals(0, doubleL.invoke(null, 1.0, 1.0));
+ assertEquals(1, doubleL.invoke(null, 2.0, 1.0));
+ assertEquals(-1, doubleL.invoke(null, 1.0, Double.NaN));
+ assertEquals(-1, doubleL.invoke(null, Double.NaN, 1.0));
+ assertEquals(-1, doubleL.invoke(null, Double.NaN, Double.NaN));
+ assertEquals(-1, doubleL.invoke(null, Double.NaN, Double.POSITIVE_INFINITY));
+ }
+
+ private <T extends Number> java.lang.reflect.Method floatingPointCompareMethod(
+ Type<T> valueType, int nanValue) throws Exception {
+ /*
+ * public static int call(float a, float b) {
+ * int result = a <=> b;
+ * return result;
+ * }
+ */
+ reset();
+ Code code = generatedType.getMethod(intType, "call", valueType, valueType)
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ Local<T> localA = code.getParameter(0, valueType);
+ Local<T> localB = code.getParameter(1, valueType);
+ Local<Integer> localResult = code.newLocal(intType);
+ code.compare(localA, localB, localResult, nanValue);
+ code.returnValue(localResult);
+ return getMethod();
+ }
+
+ public void testLongCompare() throws Exception {
+ /*
+ * public static int call(long a, long b) {
+ * int result = a <=> b;
+ * return result;
+ * }
+ */
+ Code code = generatedType.getMethod(intType, "call", longType, longType)
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ Local<Long> localA = code.getParameter(0, longType);
+ Local<Long> localB = code.getParameter(1, longType);
+ Local<Integer> localResult = code.newLocal(intType);
+ code.compare(localA, localB, localResult);
+ code.returnValue(localResult);
+
+ java.lang.reflect.Method method = getMethod();
+ assertEquals(0, method.invoke(null, Long.MIN_VALUE, Long.MIN_VALUE));
+ assertEquals(-1, method.invoke(null, Long.MIN_VALUE, 0));
+ assertEquals(-1, method.invoke(null, Long.MIN_VALUE, Long.MAX_VALUE));
+ assertEquals(1, method.invoke(null, 0, Long.MIN_VALUE));
+ assertEquals(0, method.invoke(null, 0, 0));
+ assertEquals(-1, method.invoke(null, 0, Long.MAX_VALUE));
+ assertEquals(1, method.invoke(null, Long.MAX_VALUE, Long.MIN_VALUE));
+ assertEquals(1, method.invoke(null, Long.MAX_VALUE, 0));
+ assertEquals(0, method.invoke(null, Long.MAX_VALUE, Long.MAX_VALUE));
+ }
+
+ public void testArrayLength() throws Exception {
+ java.lang.reflect.Method booleanArrayLength = arrayLengthMethod(booleanArrayType);
+ assertEquals(0, booleanArrayLength.invoke(null, new Object[] { new boolean[0] }));
+ assertEquals(5, booleanArrayLength.invoke(null, new Object[] { new boolean[5] }));
+
+ java.lang.reflect.Method intArrayLength = arrayLengthMethod(intArrayType);
+ assertEquals(0, intArrayLength.invoke(null, new Object[] { new int[0] }));
+ assertEquals(5, intArrayLength.invoke(null, new Object[] { new int[5] }));
+
+ java.lang.reflect.Method longArrayLength = arrayLengthMethod(longArrayType);
+ assertEquals(0, longArrayLength.invoke(null, new Object[] { new long[0] }));
+ assertEquals(5, longArrayLength.invoke(null, new Object[] { new long[5] }));
+
+ java.lang.reflect.Method objectArrayLength = arrayLengthMethod(objectArrayType);
+ assertEquals(0, objectArrayLength.invoke(null, new Object[] { new Object[0] }));
+ assertEquals(5, objectArrayLength.invoke(null, new Object[] { new Object[5] }));
+
+ java.lang.reflect.Method long2dArrayLength = arrayLengthMethod(long2dArrayType);
+ assertEquals(0, long2dArrayLength.invoke(null, new Object[] { new long[0][0] }));
+ assertEquals(5, long2dArrayLength.invoke(null, new Object[] { new long[5][10] }));
+ }
+
+ private <T> java.lang.reflect.Method arrayLengthMethod(Type<T> valueType) throws Exception {
+ /*
+ * public static int call(long[] array) {
+ * int result = array.length;
+ * return result;
+ * }
+ */
+ reset();
+ Code code = generatedType.getMethod(intType, "call", valueType)
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ Local<T> localArray = code.getParameter(0, valueType);
+ Local<Integer> localResult = code.newLocal(intType);
+ code.arrayLength(localArray, localResult);
+ code.returnValue(localResult);
+ return getMethod();
+ }
+
+ public void testNewArray() throws Exception {
+ java.lang.reflect.Method newBooleanArray = newArrayMethod(booleanArrayType);
+ assertEquals("[]", Arrays.toString((boolean[]) newBooleanArray.invoke(null, 0)));
+ assertEquals("[false, false, false]",
+ Arrays.toString((boolean[]) newBooleanArray.invoke(null, 3)));
+
+ java.lang.reflect.Method newIntArray = newArrayMethod(intArrayType);
+ assertEquals("[]", Arrays.toString((int[]) newIntArray.invoke(null, 0)));
+ assertEquals("[0, 0, 0]", Arrays.toString((int[]) newIntArray.invoke(null, 3)));
+
+ java.lang.reflect.Method newLongArray = newArrayMethod(longArrayType);
+ assertEquals("[]", Arrays.toString((long[]) newLongArray.invoke(null, 0)));
+ assertEquals("[0, 0, 0]", Arrays.toString((long[]) newLongArray.invoke(null, 3)));
+
+ java.lang.reflect.Method newObjectArray = newArrayMethod(objectArrayType);
+ assertEquals("[]", Arrays.toString((Object[]) newObjectArray.invoke(null, 0)));
+ assertEquals("[null, null, null]",
+ Arrays.toString((Object[]) newObjectArray.invoke(null, 3)));
+
+ java.lang.reflect.Method new2dLongArray = newArrayMethod(long2dArrayType);
+ assertEquals("[]", Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 0)));
+ assertEquals("[null, null, null]",
+ Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 3)));
+ }
+
+ private <T> java.lang.reflect.Method newArrayMethod(Type<T> valueType) throws Exception {
+ /*
+ * public static long[] call(int length) {
+ * long[] result = new long[length];
+ * return result;
+ * }
+ */
+ reset();
+ Code code = generatedType.getMethod(valueType, "call", intType)
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ Local<Integer> localLength = code.getParameter(0, intType);
+ Local<T> localResult = code.newLocal(valueType);
+ code.newArray(localLength, localResult);
+ code.returnValue(localResult);
+ return getMethod();
+ }
+
+ public void testReadAndWriteArray() throws Exception {
+ java.lang.reflect.Method swapBooleanArray = arraySwapMethod(booleanArrayType, booleanType);
+ boolean[] booleans = new boolean[3];
+ assertEquals(false, swapBooleanArray.invoke(null, booleans, 1, true));
+ assertEquals("[false, true, false]", Arrays.toString(booleans));
+
+ java.lang.reflect.Method swapIntArray = arraySwapMethod(intArrayType, intType);
+ int[] ints = new int[3];
+ assertEquals(0, swapIntArray.invoke(null, ints, 1, 5));
+ assertEquals("[0, 5, 0]", Arrays.toString(ints));
+
+ java.lang.reflect.Method swapLongArray = arraySwapMethod(longArrayType, longType);
+ long[] longs = new long[3];
+ assertEquals(0L, swapLongArray.invoke(null, longs, 1, 6L));
+ assertEquals("[0, 6, 0]", Arrays.toString(longs));
+
+ java.lang.reflect.Method swapObjectArray = arraySwapMethod(objectArrayType, objectType);
+ Object[] objects = new Object[3];
+ assertEquals(null, swapObjectArray.invoke(null, objects, 1, "X"));
+ assertEquals("[null, X, null]", Arrays.toString(objects));
+
+ java.lang.reflect.Method swapLong2dArray = arraySwapMethod(long2dArrayType, longArrayType);
+ long[][] longs2d = new long[3][];
+ assertEquals(null, swapLong2dArray.invoke(null, longs2d, 1, new long[] { 7 }));
+ assertEquals("[null, [7], null]", Arrays.deepToString(longs2d));
+ }
+
+ private <A, T> java.lang.reflect.Method arraySwapMethod(Type<A> arrayType, Type<T> singleType)
+ throws Exception {
+ /*
+ * public static long swap(long[] array, int index, long newValue) {
+ * long result = array[index];
+ * array[index] = newValue;
+ * return result;
+ * }
+ */
+ reset();
+ Code code = generatedType.getMethod(singleType, "call", arrayType, intType, singleType)
+ .declare(ACC_PUBLIC | ACC_STATIC);
+ Local<A> localArray = code.getParameter(0, arrayType);
+ Local<Integer> localIndex = code.getParameter(1, intType);
+ Local<T> localNewValue = code.getParameter(2, singleType);
+ Local<T> localResult = code.newLocal(singleType);
+ code.aget(localArray, localIndex, localResult);
+ code.aput(localArray, localIndex, localNewValue);
+ code.returnValue(localResult);
+ return getMethod();
+ }
// TODO: fail if a label is unreachable (never navigated to)
+ // TODO: more strict type parameters: Integer on methods
+
+ // TODO: don't generate multiple times (?)
+
private void addDefaultConstructor() {
Code code = generatedType.getConstructor().declare(ACC_PUBLIC | ACC_CONSTRUCTOR);
Local<?> thisRef = code.getThis(generatedType);
diff --git a/dx/src/com/android/dx/gen/Code.java b/dx/src/com/android/dx/gen/Code.java
index 6ce9e2a..756afed 100644
--- a/dx/src/com/android/dx/gen/Code.java
+++ b/dx/src/com/android/dx/gen/Code.java
@@ -29,12 +29,14 @@
import com.android.dx.rop.code.SourcePosition;
import com.android.dx.rop.code.ThrowingCstInsn;
import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.cst.CstInteger;
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_INT;
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;
@@ -62,10 +64,13 @@
private final List<Local<?>> parameters = new ArrayList<Local<?>>();
private final List<Local<?>> locals = new ArrayList<Local<?>>();
private SourcePosition sourcePosition = SourcePosition.NO_INFO;
+ private final List<Type<?>> catchTypes = new ArrayList<Type<?>>();
+ private final List<Label> catchLabels = new ArrayList<Label>();
+ private StdTypeList catches = StdTypeList.EMPTY;
public Code(Method<?, ?> method) {
this.method = method;
- thisLocal = method.isStatic()
+ this.thisLocal = method.isStatic()
? null
: new Local<Object>(this, method.declaringType, Local.InitialValue.THIS);
for (Type<?> parameter : method.parameters.types) {
@@ -114,6 +119,10 @@
* method's invocation frame, in order. Wide arguments consume two
* registers. Instance methods are passed a this reference as their
* first argument."
+ *
+ * In addition to assigning registers to each of the locals, this creates
+ * instructions to move parameters into their initial registers. These
+ * instructions are inserted before the code's first real instruction.
*/
void initializeLocals() {
if (localsInitialized) {
@@ -128,9 +137,24 @@
if (thisLocal != null) {
reg += thisLocal.initialize(reg);
}
+ int firstParamReg = reg;
+
+ List<Insn> moveParameterInstructions = new ArrayList<Insn>();
for (Local<?> local : parameters) {
+ CstInteger paramConstant = CstInteger.make(reg - firstParamReg);
reg += local.initialize(reg);
+ moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType),
+ sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant));
}
+ labels.get(0).instructions.addAll(0, moveParameterInstructions);
+ }
+
+ int paramSize() {
+ int result = 0;
+ for (Local<?> local : parameters) {
+ result += local.size();
+ }
+ return result;
}
// labels
@@ -159,10 +183,51 @@
currentLabel = label;
}
+ public void jump(Label target) {
+ addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY),
+ target);
+ }
+
+ public void addCatchClause(Type<?> throwable, Label catchClause) {
+ if (catchTypes.contains(throwable)) {
+ throw new IllegalArgumentException("Already caught: " + throwable);
+ }
+ catchTypes.add(throwable);
+ catches = toTypeList(catchTypes);
+ catchLabels.add(catchClause);
+ }
+
+ public Label removeCatchClause(Type<?> throwable) {
+ int index = catchTypes.indexOf(throwable);
+ if (index == -1) {
+ throw new IllegalArgumentException("No catch clause: " + throwable);
+ }
+ catchTypes.remove(index);
+ catches = toTypeList(catchTypes);
+ return catchLabels.remove(index);
+ }
+
+ public void throwValue(Local<?> throwable) {
+ addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition,
+ RegisterSpecList.make(throwable.spec()), catches));
+ }
+
+ private StdTypeList toTypeList(List<Type<?>> types) {
+ StdTypeList result = new StdTypeList(types.size());
+ for (int i = 0; i < types.size(); i++) {
+ result.set(i, types.get(i).ropType);
+ }
+ return result;
+ }
+
private void addInstruction(Insn insn) {
addInstruction(insn, null);
}
+ /**
+ * @param branch the branches to follow; interpretation depends on the
+ * instruction's branchingness.
+ */
private void addInstruction(Insn insn, Label branch) {
if (currentLabel == null || !currentLabel.marked) {
throw new IllegalStateException("no current label");
@@ -172,13 +237,13 @@
switch (insn.getOpcode().getBranchingness()) {
case BRANCH_NONE:
if (branch != null) {
- throw new IllegalArgumentException("branch != null");
+ throw new IllegalArgumentException("unexpected branch: " + branch);
}
return;
case BRANCH_RETURN:
if (branch != null) {
- throw new IllegalArgumentException("branch != null");
+ throw new IllegalArgumentException("unexpected branch: " + branch);
}
currentLabel = null;
break;
@@ -195,11 +260,14 @@
if (branch == null) {
throw new IllegalArgumentException("branch == null");
}
- splitCurrentLabel(branch);
+ splitCurrentLabel(branch, Collections.<Label>emptyList());
break;
case Rop.BRANCH_THROW:
- splitCurrentLabel(branch);
+ if (branch != null) {
+ throw new IllegalArgumentException("unexpected branch: " + branch);
+ }
+ splitCurrentLabel(null, new ArrayList<Label>(catchLabels));
break;
default:
@@ -209,11 +277,14 @@
/**
* Closes the current label and starts a new one.
+ *
+ * @param catchLabels an immutable list of catch labels
*/
- private void splitCurrentLabel(Label branch) {
+ private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) {
Label newLabel = newLabel();
currentLabel.primarySuccessor = newLabel;
- currentLabel.alternateSuccessor = branch;
+ currentLabel.alternateSuccessor = alternateSuccessor;
+ currentLabel.catchLabels = catchLabels;
currentLabel = newLabel;
currentLabel.marked = true;
}
@@ -227,7 +298,7 @@
RegisterSpecList.EMPTY, Constants.getConstant(value)));
} else {
addInstruction(new ThrowingCstInsn(rop, sourcePosition,
- RegisterSpecList.EMPTY, StdTypeList.EMPTY, Constants.getConstant(value)));
+ RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
moveResult(target, true);
}
}
@@ -274,7 +345,7 @@
if (rop.getBranchingness() == BRANCH_NONE) {
addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources));
} else {
- addInstruction(new ThrowingInsn(rop, sourcePosition, sources, StdTypeList.EMPTY));
+ addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches));
moveResult(target, true);
}
}
@@ -282,7 +353,7 @@
// instructions: branches
/**
- * Compare integers. If the comparison is true, execution jumps to {@code
+ * Compare ints. 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) {
@@ -294,34 +365,53 @@
RegisterSpecList.make(a.spec(), b.spec())), trueLabel);
}
- public void jump(Label target) {
- addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY),
- target);
+ /**
+ * Compare floats or doubles.
+ */
+ public <T extends Number> void compare(Local<T> a, Local<T> b, Local<Integer> target,
+ int nanValue) {
+ Rop rop;
+ if (nanValue == 1) {
+ rop = Rops.opCmpg(a.type.ropType);
+ } else if (nanValue == -1) {
+ rop = Rops.opCmpl(a.type.ropType);
+ } else {
+ throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue);
+ }
+ addInstruction(new PlainInsn(rop, sourcePosition, target.spec(),
+ RegisterSpecList.make(a.spec(), b.spec())));
+ }
+
+ /**
+ * Compare longs.
+ */
+ public <T> void compare(Local<T> a, Local<T> b, Local<?> target) {
+ addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(),
+ RegisterSpecList.make(a.spec(), b.spec())));
}
// instructions: fields
public <T, R> void iget(Field<T, R> field, Local<T> instance, Local<R> target) {
addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition,
- RegisterSpecList.make(instance.spec()), StdTypeList.EMPTY, field.constant));
+ RegisterSpecList.make(instance.spec()), catches, field.constant));
moveResult(target, true);
}
public <T, R> void iput(Field<T, R> field, Local<T> instance, Local<R> source) {
addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition,
- RegisterSpecList.make(source.spec(), instance.spec()), StdTypeList.EMPTY,
- field.constant));
+ RegisterSpecList.make(source.spec(), instance.spec()), catches, field.constant));
}
public <T> void sget(Field<?, T> field, Local<T> target) {
addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition,
- RegisterSpecList.EMPTY, StdTypeList.EMPTY, field.constant));
+ RegisterSpecList.EMPTY, catches, field.constant));
moveResult(target, true);
}
public <T> void sput(Field<?, T> field, Local<T> source) {
addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition,
- RegisterSpecList.make(source.spec()), StdTypeList.EMPTY, field.constant));
+ RegisterSpecList.make(source.spec()), catches, field.constant));
}
// instructions: invoke
@@ -331,7 +421,7 @@
throw new IllegalArgumentException();
}
addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition,
- RegisterSpecList.EMPTY, StdTypeList.EMPTY, constructor.declaringType.constant));
+ RegisterSpecList.EMPTY, catches, constructor.declaringType.constant));
moveResult(target, true);
invokeDirect(constructor, null, target, args);
}
@@ -363,7 +453,7 @@
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));
+ catches, method.constant));
if (target != null) {
moveResult(target, false);
}
@@ -373,16 +463,41 @@
public void instanceOfType(Local<?> target, Local<?> source, Type<?> type) {
addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition,
- RegisterSpecList.make(source.spec()), StdTypeList.EMPTY, type.constant));
+ RegisterSpecList.make(source.spec()), catches, 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));
+ RegisterSpecList.make(source.spec()), catches, target.type.constant));
moveResult(target, true);
}
+ // instructions: arrays
+
+ public <T> void arrayLength(Local<T> array, Local<Integer> target) {
+ addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition,
+ RegisterSpecList.make(array.spec()), catches));
+ moveResult(target, true);
+ }
+
+ public <T> void newArray(Local<Integer> length, Local<T> target) {
+ addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition,
+ RegisterSpecList.make(length.spec()), catches, target.type.constant));
+ moveResult(target, true);
+ }
+
+ public void aget(Local<?> array, Local<Integer> index, Local<?> target) {
+ addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition,
+ RegisterSpecList.make(array.spec(), index.spec()), catches));
+ moveResult(target, true);
+ }
+
+ public void aput(Local<?> array, Local<Integer> index, Local<?> source) {
+ addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition,
+ RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches));
+ }
+
// instructions: return
public void returnVoid() {
@@ -401,7 +516,7 @@
+ " but returned " + result.type);
}
addInstruction(new PlainInsn(Rops.opReturn(result.type.ropType), sourcePosition,
- null, RegisterSpecList.make(result.spec())), null);
+ null, RegisterSpecList.make(result.spec())));
}
private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) {
@@ -414,6 +529,10 @@
// produce BasicBlocks for dex
BasicBlockList toBasicBlocks() {
+ if (!localsInitialized) {
+ initializeLocals();
+ }
+
cleanUpLabels();
BasicBlockList result = new BasicBlockList(labels.size());
diff --git a/dx/src/com/android/dx/gen/Label.java b/dx/src/com/android/dx/gen/Label.java
index b68ca15..633b5f1 100644
--- a/dx/src/com/android/dx/gen/Label.java
+++ b/dx/src/com/android/dx/gen/Label.java
@@ -21,6 +21,7 @@
import com.android.dx.rop.code.InsnList;
import com.android.dx.util.IntList;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -32,6 +33,9 @@
boolean marked = false;
+ /** an immutable list of labels corresponding to the types in the catch list */
+ List<Label> catchLabels = Collections.emptyList();
+
/** contains the next instruction if no branch occurs */
Label primarySuccessor;
@@ -47,6 +51,11 @@
}
void compact() {
+ for (int i = 0; i < catchLabels.size(); i++) {
+ while (catchLabels.get(i).isEmpty()) {
+ catchLabels.set(i, catchLabels.get(i).primarySuccessor);
+ }
+ }
while (primarySuccessor != null && primarySuccessor.isEmpty()) {
primarySuccessor = primarySuccessor.primarySuccessor;
}
@@ -64,6 +73,9 @@
int primarySuccessorIndex = -1;
IntList successors = new IntList();
+ for (Label catchLabel : catchLabels) {
+ successors.add(catchLabel.id);
+ }
if (primarySuccessor != null) {
primarySuccessorIndex = primarySuccessor.id;
successors.add(primarySuccessorIndex);
diff --git a/dx/src/com/android/dx/gen/Local.java b/dx/src/com/android/dx/gen/Local.java
index db2d909..4f74b93 100644
--- a/dx/src/com/android/dx/gen/Local.java
+++ b/dx/src/com/android/dx/gen/Local.java
@@ -17,8 +17,6 @@
package com.android.dx.gen;
import com.android.dx.rop.code.RegisterSpec;
-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.
@@ -44,14 +42,11 @@
int initialize(int reg) {
this.reg = reg;
this.spec = RegisterSpec.make(reg, type.getRopType());
+ return size();
+ }
- switch (type.ropType.getBasicType()) {
- case BT_LONG:
- case BT_DOUBLE:
- return 2;
- default:
- return 1;
- }
+ int size() {
+ return type.ropType.getCategory();
}
RegisterSpec spec() {
diff --git a/dx/src/com/android/dx/gen/Method.java b/dx/src/com/android/dx/gen/Method.java
index 343510a..f8b9183 100644
--- a/dx/src/com/android/dx/gen/Method.java
+++ b/dx/src/com/android/dx/gen/Method.java
@@ -137,12 +137,10 @@
throw new IllegalStateException();
}
RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0);
- int paramSize = -1;
LocalVariableInfo locals = null;
- int positionInfo = PositionList.NONE;
- DalvCode code = RopTranslator.translate(
- ropMethod, positionInfo, locals, paramSize, dexOptions);
- return new EncodedMethod(constant, accessFlags, code, StdTypeList.EMPTY);
+ DalvCode dalvCode = RopTranslator.translate(ropMethod, PositionList.NONE, locals,
+ code.paramSize(), dexOptions);
+ return new EncodedMethod(constant, accessFlags, dalvCode, StdTypeList.EMPTY);
}
@Override public boolean equals(Object o) {
diff --git a/dx/src/com/android/dx/gen/TypeList.java b/dx/src/com/android/dx/gen/TypeList.java
index c6738fb..e18ed4a 100644
--- a/dx/src/com/android/dx/gen/TypeList.java
+++ b/dx/src/com/android/dx/gen/TypeList.java
@@ -36,10 +36,6 @@
}
}
- TypeList(List<Type<?>> types) {
- this(types.toArray(new Type[types.size()]));
- }
-
/**
* Returns an immutable list.
*/