summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2011-04-15 14:28:02 -0700
committerJesse Wilson <jessewilson@google.com>2011-04-16 07:19:47 -0700
commitda2a9cbd9df97adab3053c9831279052e490e3ac (patch)
tree3c31bd6bfb2422046f964629b5d4355f2d0b39ba
parentb665fb24d785e27480cc9b8debcf9edf4ac65d2c (diff)
downloadandroid_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.java7
-rw-r--r--dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java465
-rw-r--r--dx/src/com/android/dx/gen/BinaryOp.java36
-rw-r--r--dx/src/com/android/dx/gen/Code.java134
-rw-r--r--dx/src/com/android/dx/gen/DexGenerator.java20
-rw-r--r--dx/src/com/android/dx/gen/Field.java4
-rw-r--r--dx/src/com/android/dx/gen/Local.java29
-rw-r--r--dx/src/com/android/dx/gen/Method.java26
-rw-r--r--dx/src/com/android/dx/gen/Type.java6
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;
}