blob: 22fb3da376011bc786369b61d729fbfdb69d26b9 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.dx.gen;
import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR;
import static com.android.dx.rop.code.AccessFlags.ACC_FINAL;
import static com.android.dx.rop.code.AccessFlags.ACC_PRIVATE;
import static com.android.dx.rop.code.AccessFlags.ACC_PROTECTED;
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.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import junit.framework.TestCase;
/**
* This generates a class named 'Generated' with one or more generated methods
* and fields. In loads the generated class into the current VM and uses
* reflection to invoke its methods.
*
* <p>This test must run on a Dalvik VM.
*/
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;
private Method<Callable, Object> call;
@Override protected void setUp() throws Exception {
super.setUp();
reset();
}
/**
* The generator is mutable. Calling reset creates a new empty generator.
* This is necessary to generate multiple classes in the same test method.
*/
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);
call = callableType.getMethod(objectType, "call");
generatedType.declare("Generated.java", ACC_PUBLIC, objectType);
}
public void testNewInstance() throws Exception {
/*
* public static Constructable call(long a, boolean b) {
* Constructable result = new Constructable(a, b);
* return result;
* }
*/
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);
Constructable constructed = (Constructable) getMethod().invoke(null, 5L, false);
assertEquals(5L, constructed.a);
assertEquals(false, constructed.b);
}
public static class Constructable {
private final long a;
private final boolean b;
public Constructable(long a, boolean b) {
this.a = a;
this.b = b;
}
}
public void testInvokeStatic() throws Exception {
/*
* public static int call(int a) {
* int result = DexGeneratorTest.staticMethod(a);
* return result;
* }
*/
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<?, Integer> staticMethod
= dexGeneratorTestType.getMethod(intType, "staticMethod", intType);
code.invokeStatic(staticMethod, localResult, localA);
code.returnValue(localResult);
assertEquals(10, getMethod().invoke(null, 4));
}
@SuppressWarnings("unused") // called by generated code
public static int staticMethod(int a) {
return a + 6;
}
public void testInvokeVirtual() throws Exception {
/*
* public static int call(DexGeneratorTest test, int a) {
* int result = test.virtualMethod(a);
* return result;
* }
*/
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);
assertEquals(9, getMethod().invoke(null, this, 4));
}
@SuppressWarnings("unused") // called by generated code
public int virtualMethod(int a) {
return a + 5;
}
public <G> void testInvokeDirect() throws Exception {
/*
* private int directMethod() {
* int a = 5;
* return a;
* }
*
* public static int call(Generated g) {
* int b = g.directMethod();
* return b;
* }
*/
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);
Method<G, Integer> method = generated.getMethod(intType, "call", generated);
Code code = method.declare(ACC_PUBLIC | ACC_STATIC);
Local<Integer> localB = code.newLocal(intType);
Local<G> localG = code.getParameter(0, generated);
code.invokeDirect(directMethod, localB, localG);
code.returnValue(localB);
addDefaultConstructor();
Class<?> generatedClass = loadAndGenerate();
Object instance = generatedClass.newInstance();
java.lang.reflect.Method m = generatedClass.getMethod("call", generatedClass);
assertEquals(5, m.invoke(null, instance));
}
public <G> void testInvokeSuper() throws Exception {
/*
* public int superHashCode() {
* int result = super.hashCode();
* return result;
* }
* public int hashCode() {
* return 0;
* }
*/
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<G> localThis = superHashCode.getThis(generated);
superHashCode.invokeSuper(objectHashCode, localResult, localThis);
superHashCode.returnValue(localResult);
Code generatedHashCode = generated.getMethod(intType, "hashCode").declare(ACC_PUBLIC);
Local<Integer> localZero = generatedHashCode.newLocal(intType);
generatedHashCode.loadConstant(localZero, 0);
generatedHashCode.returnValue(localZero);
addDefaultConstructor();
Class<?> generatedClass = loadAndGenerate();
Object instance = generatedClass.newInstance();
java.lang.reflect.Method m = generatedClass.getMethod("superHashCode");
assertEquals(System.identityHashCode(instance), m.invoke(instance));
}
@SuppressWarnings("unused") // called by generated code
public int superMethod(int a) {
return a + 4;
}
public void testInvokeInterface() throws Exception {
/*
* public static Object call(Callable c) {
* Object result = c.call();
* return result;
* }
*/
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);
Callable<Object> callable = new Callable<Object>() {
public Object call() throws Exception {
return "abc";
}
};
assertEquals("abc", getMethod().invoke(null, callable));
}
public void testParameterMismatch() throws Exception {
Type<?>[] argTypes = {
generator.getType(Integer.class), // should fail because the code specifies int
objectType,
};
Method<?, Integer> method = generatedType.getMethod(intType, "call", argTypes);
Code code = method.declare(ACC_PUBLIC | ACC_STATIC);
try {
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() {
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 {
/*
* class Generated {
* public static int a;
* protected static Object b;
* }
*/
generatedType.getField(intType, "a").declare(ACC_PUBLIC | ACC_STATIC, 3);
generatedType.getField(objectType, "b").declare(ACC_PROTECTED | ACC_STATIC, null);
Class<?> generatedClass = loadAndGenerate();
java.lang.reflect.Field a = generatedClass.getField("a");
assertEquals(int.class, a.getType());
assertEquals(3, a.get(null));
java.lang.reflect.Field b = generatedClass.getDeclaredField("b");
assertEquals(Object.class, b.getType());
b.setAccessible(true);
assertEquals(null, b.get(null));
}
public void testDeclareInstanceFields() throws Exception {
/*
* class Generated {
* public int a;
* protected Object b;
* }
*/
generatedType.getField(intType, "a").declare(ACC_PUBLIC, null);
generatedType.getField(objectType, "b").declare(ACC_PROTECTED, null);
addDefaultConstructor();
Class<?> generatedClass = loadAndGenerate();
Object instance = generatedClass.newInstance();
java.lang.reflect.Field a = generatedClass.getField("a");
assertEquals(int.class, a.getType());
assertEquals(0, a.get(instance));
java.lang.reflect.Field b = generatedClass.getDeclaredField("b");
assertEquals(Object.class, b.getType());
b.setAccessible(true);
assertEquals(null, b.get(instance));
}
/**
* Declare a constructor that takes an int parameter and assigns it to a
* field.
*/
public <G> void testDeclareConstructor() throws Exception {
/*
* class Generated {
* public final int a;
* public Generated(int a) {
* this.a = a;
* }
* }
*/
Type<G> generated = generator.getType("LGenerated;");
Field<G, Integer> field = generated.getField(intType, "a");
field.declare(ACC_PUBLIC | ACC_FINAL, null);
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");
Object instance = generatedClass.getConstructor(int.class).newInstance(0xabcd);
assertEquals(0xabcd, a.get(instance));
}
public void testReturnBoolean() throws Exception {
testReturnType(boolean.class, true);
testReturnType(byte.class, (byte) 5);
testReturnType(char.class, 'E');
testReturnType(double.class, 5.0);
testReturnType(float.class, 5.0f);
testReturnType(int.class, 5);
testReturnType(long.class, 5L);
testReturnType(short.class, (short) 5);
testReturnType(void.class, null);
testReturnType(String.class, "foo");
testReturnType(Class.class, List.class);
}
private <T> void testReturnType(Class<T> javaType, T value) throws Exception {
/*
* public int call() {
* int a = 5;
* return a;
* }
*/
reset();
Type<T> returnType = generator.getType(javaType);
Code code = generatedType.getMethod(returnType, "call")
.declare(ACC_PUBLIC | ACC_STATIC);
if (value != null) {
Local<T> i = code.newLocal(returnType);
code.loadConstant(i, value);
code.returnValue(i);
} else {
code.returnVoid();
}
Class<?> generatedClass = loadAndGenerate();
java.lang.reflect.Method method = generatedClass.getMethod("call");
assertEquals(javaType, method.getReturnType());
assertEquals(value, method.invoke(null));
}
public void testBranching() throws Exception {
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 = 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 = 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 = 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 = 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 = 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 branchingMethod(Comparison comparison) throws Exception {
/*
* public static boolean call(int localA, int localB) {
* if (a comparison b) {
* return true;
* }
* return false;
* }
*/
reset();
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();
code.compare(comparison, localA, localB, ifBody);
code.jump(afterIf);
code.mark(ifBody);
code.loadConstant(result, true);
code.returnValue(result);
code.mark(afterIf);
code.loadConstant(result, false);
code.returnValue(result);
return getMethod();
}
public void testCastIntegerToInteger() throws Exception {
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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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));
assertEquals(9007199254740992.0, longToDouble.invoke(null, 9007199254740993L)); // precision
}
public void testCastFloatingPointToInteger() throws Exception {
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));
assertEquals(0, floatToInt.invoke(null, 0.5f));
assertEquals(Integer.MIN_VALUE, floatToInt.invoke(null, Float.NEGATIVE_INFINITY));
assertEquals(0, floatToInt.invoke(null, Float.NaN));
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));
assertEquals(0L, floatToLong.invoke(null, 0.5f));
assertEquals(Long.MIN_VALUE, floatToLong.invoke(null, Float.NEGATIVE_INFINITY));
assertEquals(0L, floatToLong.invoke(null, Float.NaN));
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));
assertEquals(0, doubleToInt.invoke(null, 0.5));
assertEquals(Integer.MIN_VALUE, doubleToInt.invoke(null, Double.NEGATIVE_INFINITY));
assertEquals(0, doubleToInt.invoke(null, Double.NaN));
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));
assertEquals(0L, doubleToLong.invoke(null, 0.5));
assertEquals(Long.MIN_VALUE, doubleToLong.invoke(null, Double.NEGATIVE_INFINITY));
assertEquals(0L, doubleToLong.invoke(null, Double.NaN));
}
public void testCastFloatingPointToFloatingPoint() throws Exception {
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 = 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));
assertEquals(Float.NEGATIVE_INFINITY, doubleToFloat.invoke(null, Double.NEGATIVE_INFINITY));
assertEquals(Float.NaN, doubleToFloat.invoke(null, Double.NaN));
}
private java.lang.reflect.Method numericCastingMethod(Class<?> source, Class<?> target)
throws Exception {
/*
* public static short call(int source) {
* short casted = (short) source;
* return casted;
* }
*/
reset();
Type<?> sourceType = generator.getType(source);
Type<?> targetType = generator.getType(target);
Code code = generatedType.getMethod(targetType, "call", sourceType)
.declare(ACC_PUBLIC | ACC_STATIC);
Local<?> localSource = code.getParameter(0, sourceType);
Local<?> localCasted = code.newLocal(targetType);
code.numericCast(localSource, localCasted);
code.returnValue(localCasted);
return getMethod();
}
public void testNot() throws Exception {
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 = 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 notMethod(Class<T> source) throws Exception {
/*
* public static short call(int source) {
* source = ~source;
* return not;
* }
*/
reset();
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 getMethod();
}
public void testNegate() throws Exception {
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 = 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 = 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 = 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 negateMethod(Class<T> source) throws Exception {
/*
* public static short call(int source) {
* source = -source;
* return not;
* }
*/
reset();
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 getMethod();
}
public void testIntBinaryOps() throws Exception {
java.lang.reflect.Method add = binaryOpMethod(int.class, BinaryOp.ADD);
assertEquals(79, add.invoke(null, 75, 4));
java.lang.reflect.Method subtract = binaryOpMethod(int.class, BinaryOp.SUBTRACT);
assertEquals(71, subtract.invoke(null, 75, 4));
java.lang.reflect.Method multiply = binaryOpMethod(int.class, BinaryOp.MULTIPLY);
assertEquals(300, multiply.invoke(null, 75, 4));
java.lang.reflect.Method divide = binaryOpMethod(int.class, BinaryOp.DIVIDE);
assertEquals(18, divide.invoke(null, 75, 4));
try {
divide.invoke(null, 75, 0);
fail();
} catch (InvocationTargetException expected) {
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
java.lang.reflect.Method remainder = binaryOpMethod(int.class, BinaryOp.REMAINDER);
assertEquals(3, remainder.invoke(null, 75, 4));
try {
remainder.invoke(null, 75, 0);
fail();
} catch (InvocationTargetException expected) {
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
java.lang.reflect.Method and = binaryOpMethod(int.class, BinaryOp.AND);
assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000));
java.lang.reflect.Method or = binaryOpMethod(int.class, BinaryOp.OR);
assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000));
java.lang.reflect.Method xor = binaryOpMethod(int.class, BinaryOp.XOR);
assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000));
java.lang.reflect.Method shiftLeft = binaryOpMethod(int.class, BinaryOp.SHIFT_LEFT);
assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8));
java.lang.reflect.Method shiftRight = binaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT);
assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8));
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 = binaryOpMethod(long.class, BinaryOp.ADD);
assertEquals(79L, add.invoke(null, 75L, 4L));
java.lang.reflect.Method subtract = binaryOpMethod(long.class, BinaryOp.SUBTRACT);
assertEquals(71L, subtract.invoke(null, 75L, 4L));
java.lang.reflect.Method multiply = binaryOpMethod(long.class, BinaryOp.MULTIPLY);
assertEquals(300L, multiply.invoke(null, 75L, 4L));
java.lang.reflect.Method divide = binaryOpMethod(long.class, BinaryOp.DIVIDE);
assertEquals(18L, divide.invoke(null, 75L, 4L));
try {
divide.invoke(null, 75L, 0L);
fail();
} catch (InvocationTargetException expected) {
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
java.lang.reflect.Method remainder = binaryOpMethod(long.class, BinaryOp.REMAINDER);
assertEquals(3L, remainder.invoke(null, 75L, 4L));
try {
remainder.invoke(null, 75L, 0L);
fail();
} catch (InvocationTargetException expected) {
assertEquals(ArithmeticException.class, expected.getCause().getClass());
}
java.lang.reflect.Method and = binaryOpMethod(long.class, BinaryOp.AND);
assertEquals(0xff00ff0000000000L,
and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
java.lang.reflect.Method or = binaryOpMethod(long.class, BinaryOp.OR);
assertEquals(0xffffffffff00ff00L, or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
java.lang.reflect.Method xor = binaryOpMethod(long.class, BinaryOp.XOR);
assertEquals(0x00ff00ffff00ff00L,
xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
java.lang.reflect.Method shiftLeft = binaryOpMethod(long.class, BinaryOp.SHIFT_LEFT);
assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8L));
java.lang.reflect.Method shiftRight = binaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT);
assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8L));
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 = binaryOpMethod(float.class, BinaryOp.ADD);
assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f));
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 = binaryOpMethod(float.class, BinaryOp.MULTIPLY);
assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f));
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 = 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 = binaryOpMethod(double.class, BinaryOp.ADD);
assertEquals(6.75, add.invoke(null, 5.5, 1.25));
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 = binaryOpMethod(double.class, BinaryOp.MULTIPLY);
assertEquals(6.875, multiply.invoke(null, 5.5, 1.25));
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 = 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 binaryOpMethod(Class<T> valueClass, BinaryOp op)
throws Exception {
/*
* public static int binaryOp(int a, int b) {
* int result = a + b;
* return result;
* }
*/
reset();
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 getMethod();
}
public void testReadAndWriteInstanceFields() throws Exception {
Instance instance = new Instance();
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 = instanceSwapMethod(long.class, "longValue");
instance.longValue = 500L;
assertEquals(500L, longSwap.invoke(null, instance, 1234L));
assertEquals(1234L, instance.longValue);
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 = 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 = 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 = instanceSwapMethod(Object.class, "objectValue");
instance.objectValue = "before";
assertEquals("before", objectSwap.invoke(null, instance, "after"));
assertEquals("after", instance.objectValue);
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 = instanceSwapMethod(char.class, "charValue");
instance.charValue = 'A';
assertEquals('A', charSwap.invoke(null, instance, 'B'));
assertEquals('B', instance.charValue);
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);
}
public class Instance {
public int intValue;
public long longValue;
public float floatValue;
public double doubleValue;
public Object objectValue;
public boolean booleanValue;
public byte byteValue;
public char charValue;
public short shortValue;
}
private <V> java.lang.reflect.Method instanceSwapMethod(
Class<V> valueClass, String fieldName) throws Exception {
/*
* public static int call(Instance instance, int newValue) {
* int oldValue = instance.intValue;
* instance.intValue = newValue;
* return oldValue;
* }
*/
reset();
Type<V> valueType = generator.getType(valueClass);
Type<Instance> objectType = generator.getType(Instance.class);
Field<Instance, V> field = objectType.getField(valueType, fieldName);
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 getMethod();
}
public void testReadAndWriteStaticFields() throws Exception {
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 = staticSwapMethod(long.class, "longValue");
Static.longValue = 500L;
assertEquals(500L, longSwap.invoke(null, 1234L));
assertEquals(1234L, Static.longValue);
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 = 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 = 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 = staticSwapMethod(Object.class, "objectValue");
Static.objectValue = "before";
assertEquals("before", objectSwap.invoke(null, "after"));
assertEquals("after", Static.objectValue);
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 = staticSwapMethod(char.class, "charValue");
Static.charValue = 'A';
assertEquals('A', charSwap.invoke(null, 'B'));
assertEquals('B', Static.charValue);
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);
}
public static class Static {
public static int intValue;
public static long longValue;
public static float floatValue;
public static double doubleValue;
public static Object objectValue;
public static boolean booleanValue;
public static byte byteValue;
public static char charValue;
public static short shortValue;
}
private <V> java.lang.reflect.Method staticSwapMethod(Class<V> valueClass, String fieldName)
throws Exception {
/*
* public static int call(int newValue) {
* int oldValue = Static.intValue;
* Static.intValue = newValue;
* return oldValue;
* }
*/
reset();
Type<V> valueType = generator.getType(valueClass);
Type<Static> objectType = generator.getType(Static.class);
Field<Static, V> field = objectType.getField(valueType, fieldName);
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 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));
}
/**
* Tests that we can construct a for loop.
*/
public void testForLoop() throws Exception {
/*
* public static int call(int count) {
* int result = 1;
* for (int i = 0; i < count; i += 1) {
* result = result * 2;
* }
* return result;
* }
*/
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);
Local<Integer> local2 = code.newLocal(intType);
code.loadConstant(local1, 1);
code.loadConstant(local2, 2);
code.loadConstant(localResult, 1);
code.loadConstant(localI, 0);
Label loopCondition = code.newLabel();
Label loopBody = code.newLabel();
Label afterLoop = code.newLabel();
code.mark(loopCondition);
code.compare(Comparison.LT, localI, localCount, loopBody);
code.jump(afterLoop);
code.mark(loopBody);
code.op(BinaryOp.MULTIPLY, localResult, localResult, local2);
code.op(BinaryOp.ADD, localI, localI, local1);
code.jump(loopCondition);
code.mark(afterLoop);
code.returnValue(localResult);
java.lang.reflect.Method pow2 = getMethod();
assertEquals(1, pow2.invoke(null, 0));
assertEquals(2, pow2.invoke(null, 1));
assertEquals(4, pow2.invoke(null, 2));
assertEquals(8, pow2.invoke(null, 3));
assertEquals(16, pow2.invoke(null, 4));
}
/**
* Tests that we can construct a while loop.
*/
public void testWhileLoop() throws Exception {
/*
* public static int call(int max) {
* int result = 1;
* while (result < max) {
* result = result * 2;
* }
* return result;
* }
*/
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);
code.loadConstant(local2, 2);
Label loopCondition = code.newLabel();
Label loopBody = code.newLabel();
Label afterLoop = code.newLabel();
code.mark(loopCondition);
code.compare(Comparison.LT, localResult, localMax, loopBody);
code.jump(afterLoop);
code.mark(loopBody);
code.op(BinaryOp.MULTIPLY, localResult, localResult, local2);
code.jump(loopCondition);
code.mark(afterLoop);
code.returnValue(localResult);
java.lang.reflect.Method ceilPow2 = getMethod();
assertEquals(1, ceilPow2.invoke(null, 1));
assertEquals(2, ceilPow2.invoke(null, 2));
assertEquals(4, ceilPow2.invoke(null, 3));
assertEquals(16, ceilPow2.invoke(null, 10));
assertEquals(128, ceilPow2.invoke(null, 100));
assertEquals(1024, ceilPow2.invoke(null, 1000));
}
public void testIfElseBlock() throws Exception {
/*
* public static int call(int a, int b, int c) {
* if (a < b) {
* if (a < c) {
* return a;
* } else {
* return c;
* }
* } else if (b < c) {
* return b;
* } else {
* return c;
* }
* }
*/
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();
code.compare(Comparison.LT, localA, localB, aLessThanB);
code.compare(Comparison.LT, localB, localC, bLessThanC);
code.returnValue(localC);
// (a < b)
code.mark(aLessThanB);
code.compare(Comparison.LT, localA, localC, aLessThanC);
code.returnValue(localC);
// (a < c)
code.mark(aLessThanC);
code.returnValue(localA);
// (b < c)
code.mark(bLessThanC);
code.returnValue(localB);
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));
}
public void testRecursion() throws Exception {
/*
* 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));
}
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();
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);
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);
code.invokeDirect(objectType.getConstructor(), null, thisRef);
code.returnVoid();
}
/**
* Returns the generated method.
*/
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;
}
}
throw new IllegalStateException("no call() method");
}
private Class<?> loadAndGenerate() throws IOException, ClassNotFoundException {
return generator.load(DexGeneratorTest.class.getClassLoader()).loadClass("Generated");
}
}