diff options
author | Jesse Wilson <jessewilson@google.com> | 2012-01-04 11:41:54 -0500 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2012-01-04 11:51:56 -0500 |
commit | 1609c0c6ef135b56c156b5f915ed646eb400965b (patch) | |
tree | abd2ebf68ff310f6051985869589d0b74891e349 /dx | |
parent | 26f957278b34144a3f90989ccddb0102fc1fdd62 (diff) | |
download | android_dalvik-1609c0c6ef135b56c156b5f915ed646eb400965b.tar.gz android_dalvik-1609c0c6ef135b56c156b5f915ed646eb400965b.tar.bz2 android_dalvik-1609c0c6ef135b56c156b5f915ed646eb400965b.zip |
Remove dexmaker code from dx.
This now lives as a standalone project:
http://code.google.com/p/dexmaker/
Change-Id: I4f7abff9399d6827082c9af78a015562fdbcdbb3
Diffstat (limited to 'dx')
-rw-r--r-- | dx/junit-tests/HelloWorldMaker.java | 90 | ||||
-rw-r--r-- | dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java | 1677 | ||||
-rw-r--r-- | dx/junit-tests/com/android/dx/gen/ProxyBuilderTest.java | 536 | ||||
-rw-r--r-- | dx/junit-tests/com/android/dx/gen/TypeTest.java | 30 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/BinaryOp.java | 118 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Code.java | 570 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Comparison.java | 71 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Constants.java | 72 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/DexCacheException.java | 28 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/DexGenerator.java | 281 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/FieldId.java | 71 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Label.java | 90 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Local.java | 71 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/MethodId.java | 107 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/ProxyBuilder.java | 667 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/Type.java | 140 | ||||
-rw-r--r-- | dx/src/com/android/dx/gen/TypeList.java | 64 |
17 files changed, 0 insertions, 4683 deletions
diff --git a/dx/junit-tests/HelloWorldMaker.java b/dx/junit-tests/HelloWorldMaker.java deleted file mode 100644 index 038701c77..000000000 --- a/dx/junit-tests/HelloWorldMaker.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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. - */ - -import com.android.dx.gen.BinaryOp; -import com.android.dx.gen.Code; -import com.android.dx.gen.DexGenerator; -import com.android.dx.gen.FieldId; -import com.android.dx.gen.Local; -import com.android.dx.gen.MethodId; -import com.android.dx.gen.Type; -import com.android.dx.rop.code.AccessFlags; - -import java.io.File; -import java.io.PrintStream; - -public class HelloWorldMaker { - private static final Type<PrintStream> PRINT_STREAM = Type.get(PrintStream.class); - private static final FieldId<System, PrintStream> SYSTEM_OUT - = Type.get(System.class).getField(PRINT_STREAM, "out"); - private static final MethodId<Integer, String> TO_HEX_STRING - = Type.get(Integer.class).getMethod(Type.STRING, "toHexString", Type.INT); - private static final MethodId<PrintStream, Void> PRINTLN - = PRINT_STREAM.getMethod(Type.VOID, "println", Type.STRING); - - public static void main(String[] args) throws Exception { - - /* - * This code generates Dalvik bytecode equivalent to the following - * program. - * - * public class HelloWorld { - * public static void hello() { - * int a = 0xabcd; - * int b = 0xaaaa; - * int c = a - b; - * String s = Integer.toHexString(c); - * System.out.println(s); - * } - * } - */ - - DexGenerator generator = new DexGenerator(); - - // lookup the symbols of interest - Type<?> helloWorld = Type.get("LHelloWorld;"); - MethodId hello = helloWorld.getMethod(Type.VOID, "hello"); - - // create some registers - // (I'd like a better syntax for this) - Code code = generator.declare(hello, AccessFlags.ACC_STATIC | AccessFlags.ACC_PUBLIC); - Local<Integer> a = code.newLocal(Type.INT); - Local<Integer> b = code.newLocal(Type.INT); - Local<Integer> c = code.newLocal(Type.INT); - Local<String> s = code.newLocal(Type.STRING); - Local<PrintStream> localSystemOut = code.newLocal(PRINT_STREAM); - - // specify the code instruction-by-instruction (approximately) - code.loadConstant(a, 0xabcd); - code.loadConstant(b, 0xaaaa); - code.op(BinaryOp.SUBTRACT, c, a, b); - code.invokeStatic(TO_HEX_STRING, s, c); - code.sget(SYSTEM_OUT, localSystemOut); - code.invokeVirtual(PRINTLN, null, localSystemOut, s); - code.returnVoid(); - - // TODO: create the constructor - - generator.declare(helloWorld, "Generated.java", AccessFlags.ACC_PUBLIC, Type.OBJECT); - - // load the dex - File outputDir = new File("."); - ClassLoader loader = generator.load(HelloWorldMaker.class.getClassLoader(), - outputDir, outputDir); - Class<?> helloWorldClass = loader.loadClass("HelloWorld"); - helloWorldClass.getMethod("hello").invoke(null); - } -} diff --git a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java b/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java deleted file mode 100644 index 8dc6c8db5..000000000 --- a/dx/junit-tests/com/android/dx/gen/DexGeneratorTest.java +++ /dev/null @@ -1,1677 +0,0 @@ -/* - * 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 junit.framework.TestCase; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Callable; - -/** - * 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 static Type<DexGeneratorTest> TEST_TYPE = Type.get(DexGeneratorTest.class); - private static Type<?> INT_ARRAY = Type.get(int[].class); - private static Type<boolean[]> BOOLEAN_ARRAY = Type.get(boolean[].class); - private static Type<long[]> LONG_ARRAY = Type.get(long[].class); - private static Type<Object[]> OBJECT_ARRAY = Type.get(Object[].class); - private static Type<long[][]> LONG_2D_ARRAY = Type.get(long[][].class); - private static Type<?> GENERATED = Type.get("LGenerated;"); - private static Type<Callable> CALLABLE = Type.get(Callable.class); - private static MethodId<Callable, Object> CALL = CALLABLE.getMethod(Type.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(); - generator.declare(GENERATED, "Generated.java", ACC_PUBLIC, Type.OBJECT); - } - - public void testNewInstance() throws Exception { - /* - * public static Constructable call(long a, boolean b) { - * Constructable result = new Constructable(a, b); - * return result; - * } - */ - Type<Constructable> constructable = Type.get(Constructable.class); - MethodId<?, Constructable> methodId = GENERATED.getMethod( - constructable, "call", Type.LONG, Type.BOOLEAN); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Long> localA = code.getParameter(0, Type.LONG); - Local<Boolean> localB = code.getParameter(1, Type.BOOLEAN); - MethodId<Constructable, Void> constructor - = constructable.getConstructor(Type.LONG, Type.BOOLEAN); - 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 testVoidNoArgMemberMethod() throws Exception { - /* - * public void call() { - * } - */ - MethodId<?, Void> methodId = GENERATED.getMethod(Type.VOID, "call"); - Code code = generator.declare(methodId, ACC_PUBLIC); - code.returnVoid(); - - addDefaultConstructor(); - - Class<?> generatedClass = loadAndGenerate(); - Object instance = generatedClass.newInstance(); - Method method = generatedClass.getMethod("call"); - method.invoke(instance); - } - - public void testInvokeStatic() throws Exception { - /* - * public static int call(int a) { - * int result = DexGeneratorTest.staticMethod(a); - * return result; - * } - */ - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localA = code.getParameter(0, Type.INT); - Local<Integer> localResult = code.newLocal(Type.INT); - MethodId<?, Integer> staticMethod - = TEST_TYPE.getMethod(Type.INT, "staticMethod", Type.INT); - code.invokeStatic(staticMethod, localResult, localA); - code.returnValue(localResult); - - assertEquals(10, getMethod().invoke(null, 4)); - } - - public void testCreateLocalMethodAsNull() throws Exception { - /* - * public void call(int value) { - * Method method = null; - * } - */ - MethodId<?, Void> methodId = GENERATED.getMethod(Type.VOID, "call", Type.INT); - Type<Method> methodType = Type.get(Method.class); - Code code = generator.declare(methodId, ACC_PUBLIC); - Local<Method> localMethod = code.newLocal(methodType); - code.loadConstant(localMethod, null); - code.returnVoid(); - - addDefaultConstructor(); - - Class<?> generatedClass = loadAndGenerate(); - Object instance = generatedClass.newInstance(); - Method method = generatedClass.getMethod("call", int.class); - method.invoke(instance, 0); - } - - @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; - * } - */ - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", TEST_TYPE, Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<DexGeneratorTest> localInstance = code.getParameter(0, TEST_TYPE); - Local<Integer> localA = code.getParameter(1, Type.INT); - Local<Integer> localResult = code.newLocal(Type.INT); - MethodId<DexGeneratorTest, Integer> virtualMethod - = TEST_TYPE.getMethod(Type.INT, "virtualMethod", Type.INT); - 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 = Type.get("LGenerated;"); - MethodId<G, Integer> directMethodId = generated.getMethod(Type.INT, "directMethod"); - Code directCode = generator.declare(directMethodId, ACC_PRIVATE); - directCode.getThis(generated); // 'this' is unused - Local<Integer> localA = directCode.newLocal(Type.INT); - directCode.loadConstant(localA, 5); - directCode.returnValue(localA); - - MethodId<G, Integer> methodId = generated.getMethod(Type.INT, "call", generated); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localB = code.newLocal(Type.INT); - Local<G> localG = code.getParameter(0, generated); - code.invokeDirect(directMethodId, localB, localG); - code.returnValue(localB); - - addDefaultConstructor(); - - Class<?> generatedClass = loadAndGenerate(); - Object instance = generatedClass.newInstance(); - Method method = generatedClass.getMethod("call", generatedClass); - assertEquals(5, method.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 = Type.get("LGenerated;"); - MethodId<Object, Integer> objectHashCode = Type.OBJECT.getMethod(Type.INT, "hashCode"); - Code superHashCode = generator.declare( - GENERATED.getMethod(Type.INT, "superHashCode"), ACC_PUBLIC); - Local<Integer> localResult = superHashCode.newLocal(Type.INT); - Local<G> localThis = superHashCode.getThis(generated); - superHashCode.invokeSuper(objectHashCode, localResult, localThis); - superHashCode.returnValue(localResult); - - Code generatedHashCode = generator.declare( - GENERATED.getMethod(Type.INT, "hashCode"), ACC_PUBLIC); - Local<Integer> localZero = generatedHashCode.newLocal(Type.INT); - generatedHashCode.loadConstant(localZero, 0); - generatedHashCode.returnValue(localZero); - - addDefaultConstructor(); - - Class<?> generatedClass = loadAndGenerate(); - Object instance = generatedClass.newInstance(); - Method method = generatedClass.getMethod("superHashCode"); - assertEquals(System.identityHashCode(instance), method.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; - * } - */ - MethodId<?, Object> methodId = GENERATED.getMethod(Type.OBJECT, "call", CALLABLE); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Callable> localC = code.getParameter(0, CALLABLE); - Local<Object> localResult = code.newLocal(Type.OBJECT); - 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 = { - Type.get(Integer.class), // should fail because the code specifies int - Type.OBJECT, - }; - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", argTypes); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - try { - code.getParameter(0, Type.INT); - } catch (IllegalArgumentException e) { - } - try { - code.getParameter(2, Type.INT); - } 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; - * } - */ - MethodId<?, Boolean> methodId = GENERATED.getMethod(Type.BOOLEAN, "call", TEST_TYPE); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<DexGeneratorTest> localTest = code.getParameter(0, TEST_TYPE); - Type<CharSequence> charSequenceType = Type.get(CharSequence.class); - MethodId<Object, String> objectToString = Type.OBJECT.getMethod(Type.STRING, "toString"); - MethodId<Object, Boolean> objectEquals - = Type.OBJECT.getMethod(Type.BOOLEAN, "equals", Type.OBJECT); - Local<CharSequence> localCs = code.newLocal(charSequenceType); - Local<Boolean> localResult = code.newLocal(Type.BOOLEAN); - code.invokeVirtual(objectToString, localCs, localTest); - code.invokeVirtual(objectEquals, localResult, localCs, localTest); - code.returnValue(localResult); - - assertEquals(false, getMethod().invoke(null, this)); - } - - public void testReturnTypeMismatch() { - MethodId<?, String> methodId = GENERATED.getMethod(Type.STRING, "call"); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - try { - code.returnValue(code.newLocal(Type.BOOLEAN)); - 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; - * } - */ - generator.declare(GENERATED.getField(Type.INT, "a"), ACC_PUBLIC | ACC_STATIC, 3); - generator.declare(GENERATED.getField(Type.OBJECT, "b"), ACC_PROTECTED | ACC_STATIC, null); - Class<?> generatedClass = loadAndGenerate(); - - Field a = generatedClass.getField("a"); - assertEquals(int.class, a.getType()); - assertEquals(3, a.get(null)); - - 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; - * } - */ - generator.declare(GENERATED.getField(Type.INT, "a"), ACC_PUBLIC, null); - generator.declare(GENERATED.getField(Type.OBJECT, "b"), ACC_PROTECTED, null); - - addDefaultConstructor(); - - Class<?> generatedClass = loadAndGenerate(); - Object instance = generatedClass.newInstance(); - - Field a = generatedClass.getField("a"); - assertEquals(int.class, a.getType()); - assertEquals(0, a.get(instance)); - - 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 = Type.get("LGenerated;"); - FieldId<G, Integer> fieldId = generated.getField(Type.INT, "a"); - generator.declare(fieldId, ACC_PUBLIC | ACC_FINAL, null); - MethodId<?, Void> constructor = GENERATED.getConstructor(Type.INT); - Code code = generator.declare(constructor, ACC_PUBLIC | ACC_CONSTRUCTOR); - Local<G> thisRef = code.getThis(generated); - Local<Integer> parameter = code.getParameter(0, Type.INT); - code.invokeDirect(Type.OBJECT.getConstructor(), null, thisRef); - code.iput(fieldId, thisRef, parameter); - code.returnVoid(); - - Class<?> generatedClass = loadAndGenerate(); - 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 = Type.get(javaType); - Code code = generator.declare(GENERATED.getMethod(returnType, "call"), - 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(); - Method method = generatedClass.getMethod("call"); - assertEquals(javaType, method.getReturnType()); - assertEquals(value, method.invoke(null)); - } - - public void testBranching() throws Exception { - 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)); - - 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)); - - 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)); - - 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)); - - 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)); - - 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 Method branchingMethod(Comparison comparison) throws Exception { - /* - * public static boolean call(int localA, int localB) { - * if (a comparison b) { - * return true; - * } - * return false; - * } - */ - reset(); - MethodId<?, Boolean> methodId = GENERATED.getMethod( - Type.BOOLEAN, "call", Type.INT, Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localA = code.getParameter(0, Type.INT); - Local<Integer> localB = code.getParameter(1, Type.INT); - Local<Boolean> result = code.newLocal(Type.get(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 { - 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)); - - 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)); - - 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)); - - 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)); - - 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 { - 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 - - 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)); - - 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)); - - 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 { - 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)); - - 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)); - - 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)); - - 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 { - 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)); - - 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 Method numericCastingMethod(Class<?> source, Class<?> target) - throws Exception { - /* - * public static short call(int source) { - * short casted = (short) source; - * return casted; - * } - */ - reset(); - Type<?> sourceType = Type.get(source); - Type<?> targetType = Type.get(target); - MethodId<?, ?> methodId = GENERATED.getMethod(targetType, "call", sourceType); - Code code = generator.declare(methodId, 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 { - Method notInteger = notMethod(int.class); - assertEquals(0xffffffff, notInteger.invoke(null, 0x00000000)); - assertEquals(0x00000000, notInteger.invoke(null, 0xffffffff)); - assertEquals(0xedcba987, notInteger.invoke(null, 0x12345678)); - - 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> Method notMethod(Class<T> source) throws Exception { - /* - * public static short call(int source) { - * source = ~source; - * return not; - * } - */ - reset(); - Type<T> valueType = Type.get(source); - MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType); - Code code = generator.declare(methodId, 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 { - 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)); - - 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)); - - 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)); - - 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> Method negateMethod(Class<T> source) throws Exception { - /* - * public static short call(int source) { - * source = -source; - * return not; - * } - */ - reset(); - Type<T> valueType = Type.get(source); - MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType); - Code code = generator.declare(methodId, 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 { - Method add = binaryOpMethod(int.class, BinaryOp.ADD); - assertEquals(79, add.invoke(null, 75, 4)); - - Method subtract = binaryOpMethod(int.class, BinaryOp.SUBTRACT); - assertEquals(71, subtract.invoke(null, 75, 4)); - - Method multiply = binaryOpMethod(int.class, BinaryOp.MULTIPLY); - assertEquals(300, multiply.invoke(null, 75, 4)); - - 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()); - } - - 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()); - } - - Method and = binaryOpMethod(int.class, BinaryOp.AND); - assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000)); - - Method or = binaryOpMethod(int.class, BinaryOp.OR); - assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000)); - - Method xor = binaryOpMethod(int.class, BinaryOp.XOR); - assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000)); - - Method shiftLeft = binaryOpMethod(int.class, BinaryOp.SHIFT_LEFT); - assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8)); - - Method shiftRight = binaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT); - assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8)); - - Method unsignedShiftRight = binaryOpMethod(int.class, - BinaryOp.UNSIGNED_SHIFT_RIGHT); - assertEquals(0x00abcd12, unsignedShiftRight.invoke(null, 0xabcd1234, 8)); - } - - public void testLongBinaryOps() throws Exception { - Method add = binaryOpMethod(long.class, BinaryOp.ADD); - assertEquals(79L, add.invoke(null, 75L, 4L)); - - Method subtract = binaryOpMethod(long.class, BinaryOp.SUBTRACT); - assertEquals(71L, subtract.invoke(null, 75L, 4L)); - - Method multiply = binaryOpMethod(long.class, BinaryOp.MULTIPLY); - assertEquals(300L, multiply.invoke(null, 75L, 4L)); - - 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()); - } - - 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()); - } - - Method and = binaryOpMethod(long.class, BinaryOp.AND); - assertEquals(0xff00ff0000000000L, - and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); - - Method or = binaryOpMethod(long.class, BinaryOp.OR); - assertEquals(0xffffffffff00ff00L, - or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); - - Method xor = binaryOpMethod(long.class, BinaryOp.XOR); - assertEquals(0x00ff00ffff00ff00L, - xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L)); - - Method shiftLeft = binaryOpMethod(long.class, BinaryOp.SHIFT_LEFT); - assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8L)); - - Method shiftRight = binaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT); - assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8L)); - - Method unsignedShiftRight = binaryOpMethod(long.class, - BinaryOp.UNSIGNED_SHIFT_RIGHT); - assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8L)); - } - - public void testFloatBinaryOps() throws Exception { - Method add = binaryOpMethod(float.class, BinaryOp.ADD); - assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f)); - - Method subtract = binaryOpMethod(float.class, BinaryOp.SUBTRACT); - assertEquals(4.25f, subtract.invoke(null, 5.5f, 1.25f)); - - Method multiply = binaryOpMethod(float.class, BinaryOp.MULTIPLY); - assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f)); - - 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)); - - 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 { - Method add = binaryOpMethod(double.class, BinaryOp.ADD); - assertEquals(6.75, add.invoke(null, 5.5, 1.25)); - - Method subtract = binaryOpMethod(double.class, BinaryOp.SUBTRACT); - assertEquals(4.25, subtract.invoke(null, 5.5, 1.25)); - - Method multiply = binaryOpMethod(double.class, BinaryOp.MULTIPLY); - assertEquals(6.875, multiply.invoke(null, 5.5, 1.25)); - - 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)); - - 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> 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 = Type.get(valueClass); - MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType, valueType); - Code code = generator.declare(methodId, 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(); - - Method intSwap = instanceSwapMethod(int.class, "intValue"); - instance.intValue = 5; - assertEquals(5, intSwap.invoke(null, instance, 10)); - assertEquals(10, instance.intValue); - - Method longSwap = instanceSwapMethod(long.class, "longValue"); - instance.longValue = 500L; - assertEquals(500L, longSwap.invoke(null, instance, 1234L)); - assertEquals(1234L, instance.longValue); - - Method booleanSwap = instanceSwapMethod(boolean.class, "booleanValue"); - instance.booleanValue = false; - assertEquals(false, booleanSwap.invoke(null, instance, true)); - assertEquals(true, instance.booleanValue); - - Method floatSwap = instanceSwapMethod(float.class, "floatValue"); - instance.floatValue = 1.5f; - assertEquals(1.5f, floatSwap.invoke(null, instance, 0.5f)); - assertEquals(0.5f, instance.floatValue); - - Method doubleSwap = instanceSwapMethod(double.class, "doubleValue"); - instance.doubleValue = 155.5; - assertEquals(155.5, doubleSwap.invoke(null, instance, 266.6)); - assertEquals(266.6, instance.doubleValue); - - Method objectSwap = instanceSwapMethod(Object.class, "objectValue"); - instance.objectValue = "before"; - assertEquals("before", objectSwap.invoke(null, instance, "after")); - assertEquals("after", instance.objectValue); - - Method byteSwap = instanceSwapMethod(byte.class, "byteValue"); - instance.byteValue = 0x35; - assertEquals((byte) 0x35, byteSwap.invoke(null, instance, (byte) 0x64)); - assertEquals((byte) 0x64, instance.byteValue); - - Method charSwap = instanceSwapMethod(char.class, "charValue"); - instance.charValue = 'A'; - assertEquals('A', charSwap.invoke(null, instance, 'B')); - assertEquals('B', instance.charValue); - - 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> 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 = Type.get(valueClass); - Type<Instance> objectType = Type.get(Instance.class); - FieldId<Instance, V> fieldId = objectType.getField(valueType, fieldName); - MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", objectType, valueType); - Code code = generator.declare(methodId, 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(fieldId, localInstance, localOldValue); - code.iput(fieldId, localInstance, localNewValue); - code.returnValue(localOldValue); - return getMethod(); - } - - public void testReadAndWriteStaticFields() throws Exception { - Method intSwap = staticSwapMethod(int.class, "intValue"); - Static.intValue = 5; - assertEquals(5, intSwap.invoke(null, 10)); - assertEquals(10, Static.intValue); - - Method longSwap = staticSwapMethod(long.class, "longValue"); - Static.longValue = 500L; - assertEquals(500L, longSwap.invoke(null, 1234L)); - assertEquals(1234L, Static.longValue); - - Method booleanSwap = staticSwapMethod(boolean.class, "booleanValue"); - Static.booleanValue = false; - assertEquals(false, booleanSwap.invoke(null, true)); - assertEquals(true, Static.booleanValue); - - Method floatSwap = staticSwapMethod(float.class, "floatValue"); - Static.floatValue = 1.5f; - assertEquals(1.5f, floatSwap.invoke(null, 0.5f)); - assertEquals(0.5f, Static.floatValue); - - Method doubleSwap = staticSwapMethod(double.class, "doubleValue"); - Static.doubleValue = 155.5; - assertEquals(155.5, doubleSwap.invoke(null, 266.6)); - assertEquals(266.6, Static.doubleValue); - - Method objectSwap = staticSwapMethod(Object.class, "objectValue"); - Static.objectValue = "before"; - assertEquals("before", objectSwap.invoke(null, "after")); - assertEquals("after", Static.objectValue); - - Method byteSwap = staticSwapMethod(byte.class, "byteValue"); - Static.byteValue = 0x35; - assertEquals((byte) 0x35, byteSwap.invoke(null, (byte) 0x64)); - assertEquals((byte) 0x64, Static.byteValue); - - Method charSwap = staticSwapMethod(char.class, "charValue"); - Static.charValue = 'A'; - assertEquals('A', charSwap.invoke(null, 'B')); - assertEquals('B', Static.charValue); - - 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> 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 = Type.get(valueClass); - Type<Static> objectType = Type.get(Static.class); - FieldId<Static, V> fieldId = objectType.getField(valueType, fieldName); - MethodId<?, V> methodId = GENERATED.getMethod(valueType, "call", valueType); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<V> localNewValue = code.getParameter(0, valueType); - Local<V> localOldValue = code.newLocal(valueType); - code.sget(fieldId, localOldValue); - code.sput(fieldId, localNewValue); - code.returnValue(localOldValue); - return getMethod(); - } - - public void testTypeCast() throws Exception { - /* - * public static String call(Object o) { - * String s = (String) o; - * } - */ - MethodId<?, String> methodId = GENERATED.getMethod(Type.STRING, "call", Type.OBJECT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Object> localObject = code.getParameter(0, Type.OBJECT); - Local<String> localString = code.newLocal(Type.STRING); - code.typeCast(localObject, localString); - code.returnValue(localString); - - 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; - * } - */ - MethodId<?, Boolean> methodId = GENERATED.getMethod(Type.BOOLEAN, "call", Type.OBJECT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Object> localObject = code.getParameter(0, Type.OBJECT); - Local<Boolean> localResult = code.newLocal(Type.BOOLEAN); - code.instanceOfType(localResult, localObject, Type.STRING); - code.returnValue(localResult); - - 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; - * } - */ - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localCount = code.getParameter(0, Type.INT); - Local<Integer> localResult = code.newLocal(Type.INT); - Local<Integer> localI = code.newLocal(Type.INT); - Local<Integer> local1 = code.newLocal(Type.INT); - Local<Integer> local2 = code.newLocal(Type.INT); - 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); - - 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; - * } - */ - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localMax = code.getParameter(0, Type.INT); - Local<Integer> localResult = code.newLocal(Type.INT); - Local<Integer> local2 = code.newLocal(Type.INT); - 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); - - 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; - * } - * } - */ - MethodId<?, Integer> methodId = GENERATED.getMethod( - Type.INT, "call", Type.INT, Type.INT, Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localA = code.getParameter(0, Type.INT); - Local<Integer> localB = code.getParameter(1, Type.INT); - Local<Integer> localC = code.getParameter(2, Type.INT); - 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); - - 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; - * } - */ - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localA = code.getParameter(0, Type.INT); - Local<Integer> local1 = code.newLocal(Type.INT); - Local<Integer> local2 = code.newLocal(Type.INT); - Local<Integer> localX = code.newLocal(Type.INT); - Local<Integer> localY = code.newLocal(Type.INT); - Local<Integer> localResult = code.newLocal(Type.INT); - 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(methodId, localX, localA); - code.op(BinaryOp.SUBTRACT, localA, localA, local1); - code.invokeStatic(methodId, localY, localA); - code.op(BinaryOp.ADD, localResult, localX, localY); - code.returnValue(localResult); - code.mark(baseCase); - code.returnValue(localA); - - 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"; - * } - */ - MethodId<?, String> methodId = GENERATED.getMethod(Type.STRING, "call", Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localI = code.getParameter(0, Type.INT); - Local<String> result = code.newLocal(Type.STRING); - Label catchIae = code.newLabel(); - Label catchIse = code.newLabel(); - Label catchRe = code.newLabel(); - - code.addCatchClause(Type.get(IllegalArgumentException.class), catchIae); - code.addCatchClause(Type.get(IllegalStateException.class), catchIse); - code.addCatchClause(Type.get(RuntimeException.class), catchRe); - MethodId<?, ?> thrower = TEST_TYPE.getMethod(Type.VOID, "thrower", Type.INT); - 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); - - 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"; - * } - */ - MethodId<?, String> methodId = GENERATED.getMethod( - Type.STRING, "call", Type.INT, Type.INT, Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localA = code.getParameter(0, Type.INT); - Local<Integer> localB = code.getParameter(1, Type.INT); - Local<Integer> localC = code.getParameter(2, Type.INT); - Local<String> localResult = code.newLocal(Type.STRING); - Label catchInner = code.newLabel(); - Label catchOuter = code.newLabel(); - - Type<IllegalArgumentException> iaeType = Type.get(IllegalArgumentException.class); - code.addCatchClause(iaeType, catchOuter); - - MethodId<?, ?> thrower = TEST_TYPE.getMethod(Type.VOID, "thrower", Type.INT); - 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); - - 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(); - * } - */ - MethodId<?, Void> methodId = GENERATED.getMethod(Type.VOID, "call"); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Type<IllegalStateException> iseType = Type.get(IllegalStateException.class); - MethodId<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) {} - */ - MethodId<?, Void> methodId = GENERATED.getMethod( - Type.VOID, "call", Type.INT, Type.LONG, Type.LONG); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - code.returnVoid(); - getMethod().invoke(null, 1, 2, 3); - } - - public void testFloatingPointCompare() throws Exception { - Method floatG = floatingPointCompareMethod(Type.FLOAT, 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)); - - Method floatL = floatingPointCompareMethod(Type.FLOAT, -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)); - - Method doubleG = floatingPointCompareMethod(Type.DOUBLE, 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)); - - Method doubleL = floatingPointCompareMethod(Type.DOUBLE, -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> Method floatingPointCompareMethod( - Type<T> valueType, int nanValue) throws Exception { - /* - * public static int call(float a, float b) { - * int result = a <=> b; - * return result; - * } - */ - reset(); - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", valueType, valueType); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<T> localA = code.getParameter(0, valueType); - Local<T> localB = code.getParameter(1, valueType); - Local<Integer> localResult = code.newLocal(Type.INT); - 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; - * } - */ - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", Type.LONG, Type.LONG); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Long> localA = code.getParameter(0, Type.LONG); - Local<Long> localB = code.getParameter(1, Type.LONG); - Local<Integer> localResult = code.newLocal(Type.INT); - code.compare(localA, localB, localResult); - code.returnValue(localResult); - - 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 { - Method booleanArrayLength = arrayLengthMethod(BOOLEAN_ARRAY); - assertEquals(0, booleanArrayLength.invoke(null, new Object[] { new boolean[0] })); - assertEquals(5, booleanArrayLength.invoke(null, new Object[] { new boolean[5] })); - - Method intArrayLength = arrayLengthMethod(INT_ARRAY); - assertEquals(0, intArrayLength.invoke(null, new Object[] { new int[0] })); - assertEquals(5, intArrayLength.invoke(null, new Object[] { new int[5] })); - - Method longArrayLength = arrayLengthMethod(LONG_ARRAY); - assertEquals(0, longArrayLength.invoke(null, new Object[] { new long[0] })); - assertEquals(5, longArrayLength.invoke(null, new Object[] { new long[5] })); - - Method objectArrayLength = arrayLengthMethod(OBJECT_ARRAY); - assertEquals(0, objectArrayLength.invoke(null, new Object[] { new Object[0] })); - assertEquals(5, objectArrayLength.invoke(null, new Object[] { new Object[5] })); - - Method long2dArrayLength = arrayLengthMethod(LONG_2D_ARRAY); - assertEquals(0, long2dArrayLength.invoke(null, new Object[] { new long[0][0] })); - assertEquals(5, long2dArrayLength.invoke(null, new Object[] { new long[5][10] })); - } - - private <T> Method arrayLengthMethod(Type<T> valueType) throws Exception { - /* - * public static int call(long[] array) { - * int result = array.length; - * return result; - * } - */ - reset(); - MethodId<?, Integer> methodId = GENERATED.getMethod(Type.INT, "call", valueType); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<T> localArray = code.getParameter(0, valueType); - Local<Integer> localResult = code.newLocal(Type.INT); - code.arrayLength(localArray, localResult); - code.returnValue(localResult); - return getMethod(); - } - - public void testNewArray() throws Exception { - Method newBooleanArray = newArrayMethod(BOOLEAN_ARRAY); - assertEquals("[]", Arrays.toString((boolean[]) newBooleanArray.invoke(null, 0))); - assertEquals("[false, false, false]", - Arrays.toString((boolean[]) newBooleanArray.invoke(null, 3))); - - Method newIntArray = newArrayMethod(INT_ARRAY); - assertEquals("[]", Arrays.toString((int[]) newIntArray.invoke(null, 0))); - assertEquals("[0, 0, 0]", Arrays.toString((int[]) newIntArray.invoke(null, 3))); - - Method newLongArray = newArrayMethod(LONG_ARRAY); - assertEquals("[]", Arrays.toString((long[]) newLongArray.invoke(null, 0))); - assertEquals("[0, 0, 0]", Arrays.toString((long[]) newLongArray.invoke(null, 3))); - - Method newObjectArray = newArrayMethod(OBJECT_ARRAY); - assertEquals("[]", Arrays.toString((Object[]) newObjectArray.invoke(null, 0))); - assertEquals("[null, null, null]", - Arrays.toString((Object[]) newObjectArray.invoke(null, 3))); - - Method new2dLongArray = newArrayMethod(LONG_2D_ARRAY); - assertEquals("[]", Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 0))); - assertEquals("[null, null, null]", - Arrays.deepToString((long[][]) new2dLongArray.invoke(null, 3))); - } - - private <T> Method newArrayMethod(Type<T> valueType) throws Exception { - /* - * public static long[] call(int length) { - * long[] result = new long[length]; - * return result; - * } - */ - reset(); - MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", Type.INT); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<Integer> localLength = code.getParameter(0, Type.INT); - Local<T> localResult = code.newLocal(valueType); - code.newArray(localLength, localResult); - code.returnValue(localResult); - return getMethod(); - } - - public void testReadAndWriteArray() throws Exception { - Method swapBooleanArray = arraySwapMethod(BOOLEAN_ARRAY, Type.BOOLEAN); - boolean[] booleans = new boolean[3]; - assertEquals(false, swapBooleanArray.invoke(null, booleans, 1, true)); - assertEquals("[false, true, false]", Arrays.toString(booleans)); - - Method swapIntArray = arraySwapMethod(INT_ARRAY, Type.INT); - int[] ints = new int[3]; - assertEquals(0, swapIntArray.invoke(null, ints, 1, 5)); - assertEquals("[0, 5, 0]", Arrays.toString(ints)); - - Method swapLongArray = arraySwapMethod(LONG_ARRAY, Type.LONG); - long[] longs = new long[3]; - assertEquals(0L, swapLongArray.invoke(null, longs, 1, 6L)); - assertEquals("[0, 6, 0]", Arrays.toString(longs)); - - Method swapObjectArray = arraySwapMethod(OBJECT_ARRAY, Type.OBJECT); - Object[] objects = new Object[3]; - assertEquals(null, swapObjectArray.invoke(null, objects, 1, "X")); - assertEquals("[null, X, null]", Arrays.toString(objects)); - - Method swapLong2dArray = arraySwapMethod(LONG_2D_ARRAY, LONG_ARRAY); - 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> 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(); - MethodId<?, T> methodId = GENERATED.getMethod( - singleType, "call", arrayType, Type.INT, singleType); - Code code = generator.declare(methodId, ACC_PUBLIC | ACC_STATIC); - Local<A> localArray = code.getParameter(0, arrayType); - Local<Integer> localIndex = code.getParameter(1, Type.INT); - 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 = generator.declare(GENERATED.getConstructor(), ACC_PUBLIC | ACC_CONSTRUCTOR); - Local<?> thisRef = code.getThis(GENERATED); - code.invokeDirect(Type.OBJECT.getConstructor(), null, thisRef); - code.returnVoid(); - } - - /** - * Returns the generated method. - */ - private Method getMethod() throws Exception { - Class<?> generated = loadAndGenerate(); - for (Method method : generated.getMethods()) { - if (method.getName().equals("call")) { - return method; - } - } - throw new IllegalStateException("no call() method"); - } - - public static File getDataDirectory() throws Exception { - Class<?> environmentClass = Class.forName("android.os.Environment"); - Method method = environmentClass.getMethod("getDataDirectory"); - Object dataDirectory = method.invoke(null); - return (File) dataDirectory; - } - - private Class<?> loadAndGenerate() throws Exception { - return generator.load(getClass().getClassLoader(), - getDataDirectory(), getDataDirectory()).loadClass("Generated"); - } -} diff --git a/dx/junit-tests/com/android/dx/gen/ProxyBuilderTest.java b/dx/junit-tests/com/android/dx/gen/ProxyBuilderTest.java deleted file mode 100644 index 070031cbc..000000000 --- a/dx/junit-tests/com/android/dx/gen/ProxyBuilderTest.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * 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 junit.framework.AssertionFailedError; -import junit.framework.TestCase; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.Random; - -public class ProxyBuilderTest extends TestCase { - private FakeInvocationHandler fakeHandler = new FakeInvocationHandler(); - - public static class SimpleClass { - public String simpleMethod() { - throw new AssertionFailedError(); - } - } - - public void testExampleOperation() throws Throwable { - fakeHandler.setFakeResult("expected"); - SimpleClass proxy = proxyFor(SimpleClass.class).build(); - assertEquals("expected", proxy.simpleMethod()); - } - - public static class ConstructorTakesArguments { - private final String argument; - - public ConstructorTakesArguments(String arg) { - argument = arg; - } - - public String method() { - throw new AssertionFailedError(); - } - } - - public void testConstruction_SucceedsIfCorrectArgumentsProvided() throws Throwable { - ConstructorTakesArguments proxy = proxyFor(ConstructorTakesArguments.class) - .constructorArgTypes(String.class) - .constructorArgValues("hello") - .build(); - assertEquals("hello", proxy.argument); - proxy.method(); - } - - public void testConstruction_FailsWithWrongNumberOfArguments() throws Throwable { - try { - proxyFor(ConstructorTakesArguments.class).build(); - fail(); - } catch (IllegalArgumentException expected) {} - } - - public void testClassIsNotAccessbile_FailsWithUnsupportedOperationException() throws Exception { - class MethodVisibilityClass { - } - try { - proxyFor(MethodVisibilityClass.class).build(); - fail(); - } catch (UnsupportedOperationException expected) {} - } - - private static class PrivateVisibilityClass { - } - - public void testPrivateClass_FailsWithUnsupportedOperationException() throws Exception { - try { - proxyFor(PrivateVisibilityClass.class).build(); - fail(); - } catch (UnsupportedOperationException expected) {} - } - - protected static class ProtectedVisibilityClass { - public String foo() { - throw new AssertionFailedError(); - } - } - - public void testProtectedVisibility_WorksFine() throws Exception { - assertEquals("fake result", proxyFor(ProtectedVisibilityClass.class).build().foo()); - } - - public static class HasFinalMethod { - public String nonFinalMethod() { - return "non-final method"; - } - - public final String finalMethod() { - return "final method"; - } - } - - public void testCanProxyClassesWithFinalMethods_WillNotCallTheFinalMethod() throws Throwable { - HasFinalMethod proxy = proxyFor(HasFinalMethod.class).build(); - assertEquals("final method", proxy.finalMethod()); - assertEquals("fake result", proxy.nonFinalMethod()); - } - - public static class HasPrivateMethod { - private String result() { - return "expected"; - } - } - - public void testProxyingPrivateMethods_NotIntercepted() throws Throwable { - assertEquals("expected", proxyFor(HasPrivateMethod.class).build().result()); - } - - public static class HasPackagePrivateMethod { - String result() { - throw new AssertionFailedError(); - } - } - - public void testProxyingPackagePrivateMethods_AreIntercepted() throws Throwable { - assertEquals("fake result", proxyFor(HasPackagePrivateMethod.class).build().result()); - } - - public static class HasProtectedMethod { - protected String result() { - throw new AssertionFailedError(); - } - } - - public void testProxyingProtectedMethods_AreIntercepted() throws Throwable { - assertEquals("fake result", proxyFor(HasProtectedMethod.class).build().result()); - } - - public static class HasVoidMethod { - public void dangerousMethod() { - fail(); - } - } - - public void testVoidMethod_ShouldNotThrowRuntimeException() throws Throwable { - proxyFor(HasVoidMethod.class).build().dangerousMethod(); - } - - public void testObjectMethodsAreAlsoProxied() throws Throwable { - Object proxy = proxyFor(Object.class).build(); - fakeHandler.setFakeResult("mystring"); - assertEquals("mystring", proxy.toString()); - fakeHandler.setFakeResult(-1); - assertEquals(-1, proxy.hashCode()); - fakeHandler.setFakeResult(false); - assertEquals(false, proxy.equals(proxy)); - } - - public static class AllPrimitiveMethods { - public boolean getBoolean() { return true; } - public int getInt() { return 1; } - public byte getByte() { return 2; } - public long getLong() { return 3L; } - public short getShort() { return 4; } - public float getFloat() { return 5f; } - public double getDouble() { return 6.0; } - public char getChar() { return 'c'; } - } - - public void testAllPrimitiveReturnTypes() throws Throwable { - AllPrimitiveMethods proxy = proxyFor(AllPrimitiveMethods.class).build(); - fakeHandler.setFakeResult(false); - assertEquals(false, proxy.getBoolean()); - fakeHandler.setFakeResult(8); - assertEquals(8, proxy.getInt()); - fakeHandler.setFakeResult((byte) 9); - assertEquals(9, proxy.getByte()); - fakeHandler.setFakeResult(10L); - assertEquals(10, proxy.getLong()); - fakeHandler.setFakeResult((short) 11); - assertEquals(11, proxy.getShort()); - fakeHandler.setFakeResult(12f); - assertEquals(12f, proxy.getFloat()); - fakeHandler.setFakeResult(13.0); - assertEquals(13.0, proxy.getDouble()); - fakeHandler.setFakeResult('z'); - assertEquals('z', proxy.getChar()); - } - - public static class PassThroughAllPrimitives { - public boolean getBoolean(boolean input) { return input; } - public int getInt(int input) { return input; } - public byte getByte(byte input) { return input; } - public long getLong(long input) { return input; } - public short getShort(short input) { return input; } - public float getFloat(float input) { return input; } - public double getDouble(double input) { return input; } - public char getChar(char input) { return input; } - public String getString(String input) { return input; } - public Object getObject(Object input) { return input; } - public void getNothing() {} - } - - public static class InvokeSuperHandler implements InvocationHandler { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return ProxyBuilder.callSuper(proxy, method, args); - } - } - - public void testPassThroughWorksForAllPrimitives() throws Exception { - PassThroughAllPrimitives proxy = proxyFor(PassThroughAllPrimitives.class) - .handler(new InvokeSuperHandler()) - .build(); - assertEquals(false, proxy.getBoolean(false)); - assertEquals(true, proxy.getBoolean(true)); - assertEquals(0, proxy.getInt(0)); - assertEquals(1, proxy.getInt(1)); - assertEquals((byte) 2, proxy.getByte((byte) 2)); - assertEquals((byte) 3, proxy.getByte((byte) 3)); - assertEquals(4L, proxy.getLong(4L)); - assertEquals(5L, proxy.getLong(5L)); - assertEquals((short) 6, proxy.getShort((short) 6)); - assertEquals((short) 7, proxy.getShort((short) 7)); - assertEquals(8f, proxy.getFloat(8f)); - assertEquals(9f, proxy.getFloat(9f)); - assertEquals(10.0, proxy.getDouble(10.0)); - assertEquals(11.0, proxy.getDouble(11.0)); - assertEquals('a', proxy.getChar('a')); - assertEquals('b', proxy.getChar('b')); - assertEquals("asdf", proxy.getString("asdf")); - assertEquals("qwer", proxy.getString("qwer")); - assertEquals(null, proxy.getString(null)); - Object a = new Object(); - assertEquals(a, proxy.getObject(a)); - assertEquals(null, proxy.getObject(null)); - proxy.getNothing(); - } - - public static class ExtendsAllPrimitiveMethods extends AllPrimitiveMethods { - public int example() { return 0; } - } - - public void testProxyWorksForSuperclassMethodsAlso() throws Throwable { - ExtendsAllPrimitiveMethods proxy = proxyFor(ExtendsAllPrimitiveMethods.class).build(); - fakeHandler.setFakeResult(99); - assertEquals(99, proxy.example()); - assertEquals(99, proxy.getInt()); - assertEquals(99, proxy.hashCode()); - } - - public static class HasOddParams { - public long method(int first, Integer second) { - throw new AssertionFailedError(); - } - } - - public void testMixingBoxedAndUnboxedParams() throws Throwable { - HasOddParams proxy = proxyFor(HasOddParams.class).build(); - fakeHandler.setFakeResult(99L); - assertEquals(99L, proxy.method(1, Integer.valueOf(2))); - } - - public static class SingleInt { - public String getString(int value) { - throw new AssertionFailedError(); - } - } - - public void testSinglePrimitiveParameter() throws Throwable { - InvocationHandler handler = new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return "asdf" + ((Integer) args[0]).intValue(); - } - }; - assertEquals("asdf1", proxyFor(SingleInt.class).handler(handler).build().getString(1)); - } - - public static class TwoConstructors { - private final String string; - - public TwoConstructors() { - string = "no-arg"; - } - - public TwoConstructors(boolean unused) { - string = "one-arg"; - } - } - - public void testNoConstructorArguments_CallsNoArgConstructor() throws Throwable { - TwoConstructors twoConstructors = proxyFor(TwoConstructors.class).build(); - assertEquals("no-arg", twoConstructors.string); - } - - public void testWithoutInvocationHandler_ThrowsIllegalArgumentException() throws Throwable { - try { - ProxyBuilder.forClass(TwoConstructors.class) - .dexCache(DexGeneratorTest.getDataDirectory()) - .build(); - fail(); - } catch (IllegalArgumentException expected) {} - } - - public static class HardToConstructCorrectly { - public HardToConstructCorrectly() { fail(); } - public HardToConstructCorrectly(Runnable ignored) { fail(); } - public HardToConstructCorrectly(Exception ignored) { fail(); } - public HardToConstructCorrectly(Boolean ignored) { /* safe */ } - public HardToConstructCorrectly(Integer ignored) { fail(); } - } - - public void testHardToConstruct_WorksIfYouSpecifyTheConstructorCorrectly() throws Throwable { - proxyFor(HardToConstructCorrectly.class) - .constructorArgTypes(Boolean.class) - .constructorArgValues(true) - .build(); - } - - public void testHardToConstruct_EvenWorksWhenArgsAreAmbiguous() throws Throwable { - proxyFor(HardToConstructCorrectly.class) - .constructorArgTypes(Boolean.class) - .constructorArgValues(new Object[] { null }) - .build(); - } - - public void testHardToConstruct_DoesNotInferTypesFromValues() throws Throwable { - try { - proxyFor(HardToConstructCorrectly.class) - .constructorArgValues(true) - .build(); - fail(); - } catch (IllegalArgumentException expected) {} - } - - public void testDefaultProxyHasSuperMethodToAccessOriginal() throws Exception { - Object objectProxy = proxyFor(Object.class).build(); - assertNotNull(objectProxy.getClass().getMethod("super_hashCode")); - } - - public static class PrintsOddAndValue { - public String method(int value) { - return "odd " + value; - } - } - - public void testSometimesDelegateToSuper() throws Exception { - InvocationHandler delegatesOddValues = new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getName().equals("method")) { - int intValue = ((Integer) args[0]).intValue(); - if (intValue % 2 == 0) { - return "even " + intValue; - } - } - return ProxyBuilder.callSuper(proxy, method, args); - } - }; - PrintsOddAndValue proxy = proxyFor(PrintsOddAndValue.class) - .handler(delegatesOddValues) - .build(); - assertEquals("even 0", proxy.method(0)); - assertEquals("odd 1", proxy.method(1)); - assertEquals("even 2", proxy.method(2)); - assertEquals("odd 3", proxy.method(3)); - } - - public static class DoubleReturn { - public double getValue() { - return 2.0; - } - } - - public void testUnboxedResult() throws Exception { - fakeHandler.fakeResult = 2.0; - assertEquals(2.0, proxyFor(DoubleReturn.class).build().getValue()); - } - - public static void staticMethod() { - } - - public void testDoesNotOverrideStaticMethods() throws Exception { - // Method should exist on this test class itself. - ProxyBuilderTest.class.getDeclaredMethod("staticMethod"); - // Method should not exist on the subclass. - try { - proxyFor(ProxyBuilderTest.class).build().getClass().getDeclaredMethod("staticMethod"); - fail(); - } catch (NoSuchMethodException expected) {} - } - - public void testIllegalCacheDirectory() throws Exception { - try { - proxyFor(Object.class).dexCache(new File("//////")).build(); - fail(); - } catch (DexCacheException expected) {} - } - - public void testInvalidConstructorSpecification() throws Exception { - try { - proxyFor(Object.class) - .constructorArgTypes(String.class, Boolean.class) - .constructorArgValues("asdf", true) - .build(); - fail(); - } catch (IllegalArgumentException expected) {} - } - - public static abstract class AbstractClass { - public abstract Object getValue(); - } - - public void testAbstractClassBehaviour() throws Exception { - assertEquals("fake result", proxyFor(AbstractClass.class).build().getValue()); - } - - public static class CtorHasDeclaredException { - public CtorHasDeclaredException() throws IOException { - throw new IOException(); - } - } - - public static class CtorHasRuntimeException { - public CtorHasRuntimeException() { - throw new RuntimeException("my message"); - } - } - - public static class CtorHasError { - public CtorHasError() { - throw new Error("my message again"); - } - } - - public void testParentConstructorThrowsDeclaredException() throws Exception { - try { - proxyFor(CtorHasDeclaredException.class).build(); - fail(); - } catch (UndeclaredThrowableException expected) { - assertTrue(expected.getCause() instanceof IOException); - } - try { - proxyFor(CtorHasRuntimeException.class).build(); - fail(); - } catch (RuntimeException expected) { - assertEquals("my message", expected.getMessage()); - } - try { - proxyFor(CtorHasError.class).build(); - fail(); - } catch (Error expected) { - assertEquals("my message again", expected.getMessage()); - } - } - - public void testGetInvocationHandler_NormalOperation() throws Exception { - Object proxy = proxyFor(Object.class).build(); - assertSame(fakeHandler, ProxyBuilder.getInvocationHandler(proxy)); - } - - public void testGetInvocationHandler_NotAProxy() { - try { - ProxyBuilder.getInvocationHandler(new Object()); - fail(); - } catch (IllegalArgumentException expected) {} - } - - public static class ReturnsObject { - public Object getValue() { - return new Object(); - } - } - - public static class ReturnsString extends ReturnsObject { - @Override - public String getValue() { - return "a string"; - } - } - - public void testCovariantReturnTypes_NormalBehaviour() throws Exception { - String expected = "some string"; - fakeHandler.setFakeResult(expected); - assertSame(expected, proxyFor(ReturnsObject.class).build().getValue()); - assertSame(expected, proxyFor(ReturnsString.class).build().getValue()); - } - - public void testCovariantReturnTypes_WrongReturnType() throws Exception { - try { - fakeHandler.setFakeResult(new Object()); - proxyFor(ReturnsString.class).build().getValue(); - fail(); - } catch (ClassCastException expected) {} - } - - public void testCaching_ShouldWork() { - // TODO: We're not supporting caching yet. But we should as soon as possible. - fail(); - } - - public void testSubclassOfRandom() throws Exception { - proxyFor(Random.class) - .handler(new InvokeSuperHandler()) - .build(); - } - - /** Simple helper to add the most common args for this test to the proxy builder. */ - private <T> ProxyBuilder<T> proxyFor(Class<T> clazz) throws Exception { - return ProxyBuilder.forClass(clazz) - .handler(fakeHandler) - .dexCache(DexGeneratorTest.getDataDirectory()); - } - - private static class FakeInvocationHandler implements InvocationHandler { - private Object fakeResult = "fake result"; - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return fakeResult; - } - - public void setFakeResult(Object result) { - fakeResult = result; - } - } -} diff --git a/dx/junit-tests/com/android/dx/gen/TypeTest.java b/dx/junit-tests/com/android/dx/gen/TypeTest.java deleted file mode 100644 index a7e17e8cf..000000000 --- a/dx/junit-tests/com/android/dx/gen/TypeTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 junit.framework.TestCase; - -public final class TypeTest extends TestCase { - public void testGetType() { - assertEquals("Ljava/lang/String;", Type.get(String.class).getName()); - assertEquals("[Ljava/lang/String;", Type.get(String[].class).getName()); - assertEquals("[[Ljava/lang/String;", Type.get(String[][].class).getName()); - assertEquals("I", Type.get(int.class).getName()); - assertEquals("[I", Type.get(int[].class).getName()); - assertEquals("[[I", Type.get(int[][].class).getName()); - } -} diff --git a/dx/src/com/android/dx/gen/BinaryOp.java b/dx/src/com/android/dx/gen/BinaryOp.java deleted file mode 100644 index 65a29983a..000000000 --- a/dx/src/com/android/dx/gen/BinaryOp.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 com.android.dx.rop.code.Rop; -import com.android.dx.rop.code.Rops; -import com.android.dx.rop.type.TypeList; - -/** - * 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); - } - }; - - abstract Rop rop(com.android.dx.rop.type.TypeList types); -} diff --git a/dx/src/com/android/dx/gen/Code.java b/dx/src/com/android/dx/gen/Code.java deleted file mode 100644 index 3868cd3b5..000000000 --- a/dx/src/com/android/dx/gen/Code.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * 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 com.android.dx.rop.code.BasicBlockList; -import com.android.dx.rop.code.Insn; -import com.android.dx.rop.code.PlainCstInsn; -import com.android.dx.rop.code.PlainInsn; -import com.android.dx.rop.code.RegisterSpecList; -import com.android.dx.rop.code.Rop; -import static com.android.dx.rop.code.Rop.BRANCH_GOTO; -import static com.android.dx.rop.code.Rop.BRANCH_NONE; -import static com.android.dx.rop.code.Rop.BRANCH_RETURN; -import com.android.dx.rop.code.Rops; -import com.android.dx.rop.code.SourcePosition; -import com.android.dx.rop.code.ThrowingCstInsn; -import com.android.dx.rop.code.ThrowingInsn; -import com.android.dx.rop.cst.CstInteger; -import com.android.dx.rop.type.StdTypeList; -import static com.android.dx.rop.type.Type.BT_BYTE; -import static com.android.dx.rop.type.Type.BT_CHAR; -import static com.android.dx.rop.type.Type.BT_INT; -import static com.android.dx.rop.type.Type.BT_SHORT; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * Builds a sequence of instructions. - */ -public final class Code { - private final MethodId<?, ?> 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. - */ - private final List<Label> labels = new ArrayList<Label>(); - - /** - * The label currently receiving instructions. This is null if the most - * recent instruction was a return or goto. - */ - private Label currentLabel; - - /** 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; - private final List<Type<?>> catchTypes = new ArrayList<Type<?>>(); - private final List<Label> catchLabels = new ArrayList<Label>(); - private StdTypeList catches = StdTypeList.EMPTY; - - Code(DexGenerator.MethodDeclaration methodDeclaration) { - this.method = methodDeclaration.method; - this.thisLocal = methodDeclaration.isStatic() - ? null - : Local.get(this, method.declaringType); - for (Type<?> parameter : method.parameters.types) { - parameters.add(Local.get(this, parameter)); - } - this.currentLabel = newLabel(); - this.currentLabel.marked = true; - } - - public <T> Local<T> newLocal(Type<T> type) { - if (localsInitialized) { - throw new IllegalStateException("Cannot allocate locals after adding instructions"); - } - Local<T> result = Local.get(this, type); - locals.add(result); - return result; - } - - public <T> Local<T> getParameter(int index, Type<T> type) { - return coerce(parameters.get(index), type); - } - - 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; - } - - /** - * 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." - * - * In addition to assigning registers to each of the locals, this creates - * instructions to move parameters into their initial registers. These - * instructions are inserted before the code's first real instruction. - */ - void initializeLocals() { - if (localsInitialized) { - throw new AssertionError(); - } - localsInitialized = true; - - int reg = 0; - for (Local<?> local : locals) { - reg += local.initialize(reg); - } - if (thisLocal != null) { - reg += thisLocal.initialize(reg); - } - int firstParamReg = reg; - - List<Insn> moveParameterInstructions = new ArrayList<Insn>(); - for (Local<?> local : parameters) { - CstInteger paramConstant = CstInteger.make(reg - firstParamReg); - reg += local.initialize(reg); - moveParameterInstructions.add(new PlainCstInsn(Rops.opMoveParam(local.type.ropType), - sourcePosition, local.spec(), RegisterSpecList.EMPTY, paramConstant)); - } - labels.get(0).instructions.addAll(0, moveParameterInstructions); - } - - int paramSize() { - int result = 0; - for (Local<?> local : parameters) { - result += local.size(); - } - return result; - } - - // labels - - /** - * Creates a new label for use as a branch target. The new label must have - * code attached to it later by calling {@link #mark(Label)}. - */ - public Label newLabel() { - Label result = new Label(); - labels.add(result); - return result; - } - - /** - * Start defining instructions for the named label. - */ - public void mark(Label label) { - if (label.marked) { - throw new IllegalStateException("already marked"); - } - label.marked = true; - if (currentLabel != null) { - jump(label); // blocks must end with a branch, return or throw - } - currentLabel = label; - } - - public void jump(Label target) { - addInstruction(new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY), - target); - } - - public void addCatchClause(Type<?> throwable, Label catchClause) { - if (catchTypes.contains(throwable)) { - throw new IllegalArgumentException("Already caught: " + throwable); - } - catchTypes.add(throwable); - catches = toTypeList(catchTypes); - catchLabels.add(catchClause); - } - - public Label removeCatchClause(Type<?> throwable) { - int index = catchTypes.indexOf(throwable); - if (index == -1) { - throw new IllegalArgumentException("No catch clause: " + throwable); - } - catchTypes.remove(index); - catches = toTypeList(catchTypes); - return catchLabels.remove(index); - } - - public void throwValue(Local<?> throwable) { - addInstruction(new ThrowingInsn(Rops.THROW, sourcePosition, - RegisterSpecList.make(throwable.spec()), catches)); - } - - private StdTypeList toTypeList(List<Type<?>> types) { - StdTypeList result = new StdTypeList(types.size()); - for (int i = 0; i < types.size(); i++) { - result.set(i, types.get(i).ropType); - } - return result; - } - - private void addInstruction(Insn insn) { - addInstruction(insn, null); - } - - /** - * @param branch the branches to follow; interpretation depends on the - * instruction's branchingness. - */ - private void addInstruction(Insn insn, Label branch) { - if (currentLabel == null || !currentLabel.marked) { - throw new IllegalStateException("no current label"); - } - currentLabel.instructions.add(insn); - - switch (insn.getOpcode().getBranchingness()) { - case BRANCH_NONE: - if (branch != null) { - throw new IllegalArgumentException("unexpected branch: " + branch); - } - return; - - case BRANCH_RETURN: - if (branch != null) { - throw new IllegalArgumentException("unexpected branch: " + branch); - } - currentLabel = null; - break; - - case BRANCH_GOTO: - if (branch == null) { - throw new IllegalArgumentException("branch == null"); - } - currentLabel.primarySuccessor = branch; - currentLabel = null; - break; - - case Rop.BRANCH_IF: - if (branch == null) { - throw new IllegalArgumentException("branch == null"); - } - splitCurrentLabel(branch, Collections.<Label>emptyList()); - break; - - case Rop.BRANCH_THROW: - if (branch != null) { - throw new IllegalArgumentException("unexpected branch: " + branch); - } - splitCurrentLabel(null, new ArrayList<Label>(catchLabels)); - break; - - default: - throw new IllegalArgumentException(); - } - } - - /** - * Closes the current label and starts a new one. - * - * @param catchLabels an immutable list of catch labels - */ - private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels) { - Label newLabel = newLabel(); - currentLabel.primarySuccessor = newLabel; - currentLabel.alternateSuccessor = alternateSuccessor; - currentLabel.catchLabels = catchLabels; - currentLabel = newLabel; - currentLabel.marked = true; - } - - // instructions: constants - - public <T> void loadConstant(Local<T> target, T value) { - Rop rop = value == null - ? Rops.CONST_OBJECT_NOTHROW - : Rops.opConst(target.type.ropType); - if (rop.getBranchingness() == BRANCH_NONE) { - addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(), - RegisterSpecList.EMPTY, Constants.getConstant(value))); - } else { - addInstruction(new ThrowingCstInsn(rop, sourcePosition, - RegisterSpecList.EMPTY, catches, Constants.getConstant(value))); - moveResult(target, true); - } - } - - // instructions: unary - - public <T> void negate(Local<T> source, Local<T> target) { - unary(Rops.opNeg(source.type.ropType), source, target); - } - - public <T> void not(Local<T> source, Local<T> target) { - unary(Rops.opNot(source.type.ropType), source, target); - } - - public void numericCast(Local<?> source, Local<?> target) { - unary(getCastRop(source.type.ropType, target.type.ropType), source, target); - } - - private Rop getCastRop(com.android.dx.rop.type.Type sourceType, - com.android.dx.rop.type.Type targetType) { - if (sourceType.getBasicType() == BT_INT) { - switch (targetType.getBasicType()) { - case BT_SHORT: - return Rops.TO_SHORT; - case BT_CHAR: - return Rops.TO_CHAR; - case BT_BYTE: - return Rops.TO_BYTE; - } - } - return Rops.opConv(targetType, sourceType); - } - - private void unary(Rop rop, Local<?> source, Local<?> target) { - addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), source.spec())); - } - - // instructions: binary - - public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) { - Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); - RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec()); - - if (rop.getBranchingness() == BRANCH_NONE) { - addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), sources)); - } else { - addInstruction(new ThrowingInsn(rop, sourcePosition, sources, catches)); - moveResult(target, true); - } - } - - // instructions: branches - - /** - * Compare ints. If the comparison is true, execution jumps to {@code - * trueLabel}. If it is false, execution continues to the next instruction. - */ - public <T> void compare(Comparison comparison, Local<T> a, Local<T> b, Label trueLabel) { - if (trueLabel == null) { - throw new IllegalArgumentException(); - } - Rop rop = comparison.rop(StdTypeList.make(a.type.ropType, b.type.ropType)); - addInstruction(new PlainInsn(rop, sourcePosition, null, - RegisterSpecList.make(a.spec(), b.spec())), trueLabel); - } - - /** - * Compare floats or doubles. - */ - public <T extends Number> void compare(Local<T> a, Local<T> b, Local<Integer> target, - int nanValue) { - Rop rop; - if (nanValue == 1) { - rop = Rops.opCmpg(a.type.ropType); - } else if (nanValue == -1) { - rop = Rops.opCmpl(a.type.ropType); - } else { - throw new IllegalArgumentException("expected 1 or -1 but was " + nanValue); - } - addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), - RegisterSpecList.make(a.spec(), b.spec()))); - } - - /** - * Compare longs. - */ - public <T> void compare(Local<T> a, Local<T> b, Local<?> target) { - addInstruction(new PlainInsn(Rops.CMPL_LONG, sourcePosition, target.spec(), - RegisterSpecList.make(a.spec(), b.spec()))); - } - - // instructions: fields - - public <D, V> void iget(FieldId<D, V> fieldId, Local<D> instance, Local<V> target) { - addInstruction(new ThrowingCstInsn(Rops.opGetField(target.type.ropType), sourcePosition, - RegisterSpecList.make(instance.spec()), catches, fieldId.constant)); - moveResult(target, true); - } - - public <D, V> void iput(FieldId<D, V> fieldId, Local<D> instance, Local<V> source) { - addInstruction(new ThrowingCstInsn(Rops.opPutField(source.type.ropType), sourcePosition, - RegisterSpecList.make(source.spec(), instance.spec()), catches, fieldId.constant)); - } - - public <V> void sget(FieldId<?, V> fieldId, Local<V> target) { - addInstruction(new ThrowingCstInsn(Rops.opGetStatic(target.type.ropType), sourcePosition, - RegisterSpecList.EMPTY, catches, fieldId.constant)); - moveResult(target, true); - } - - public <V> void sput(FieldId<?, V> fieldId, Local<V> source) { - addInstruction(new ThrowingCstInsn(Rops.opPutStatic(source.type.ropType), sourcePosition, - RegisterSpecList.make(source.spec()), catches, fieldId.constant)); - } - - // instructions: invoke - - public <T> void newInstance(Local<T> target, MethodId<T, Void> constructor, Local<?>... args) { - if (target == null) { - throw new IllegalArgumentException(); - } - addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition, - RegisterSpecList.EMPTY, catches, constructor.declaringType.constant)); - moveResult(target, true); - invokeDirect(constructor, null, target, args); - } - - public <R> void invokeStatic(MethodId<?, R> method, Local<? super R> target, Local<?>... args) { - invoke(Rops.opInvokeStatic(method.prototype(true)), method, target, null, args); - } - - public <D, R> void invokeVirtual(MethodId<D, R> method, Local<? super R> target, - Local<? extends D> object, Local<?>... args) { - invoke(Rops.opInvokeVirtual(method.prototype(true)), method, target, object, args); - } - - public <D, R> void invokeDirect(MethodId<D, R> method, Local<? super R> target, - Local<? extends D> object, Local<?>... args) { - invoke(Rops.opInvokeDirect(method.prototype(true)), method, target, object, args); - } - - public <D, R> void invokeSuper(MethodId<D, R> method, Local<? super R> target, - Local<? extends D> object, Local<?>... args) { - invoke(Rops.opInvokeSuper(method.prototype(true)), method, target, object, args); - } - - public <D, R> void invokeInterface(MethodId<D, R> method, Local<? super R> target, - Local<? extends D> object, Local<?>... args) { - invoke(Rops.opInvokeInterface(method.prototype(true)), method, target, object, args); - } - - private <D, R> void invoke(Rop rop, MethodId<D, R> method, Local<? super R> target, - Local<? extends D> object, Local<?>... args) { - addInstruction(new ThrowingCstInsn(rop, sourcePosition, concatenate(object, args), - catches, method.constant)); - if (target != null) { - moveResult(target, false); - } - } - - // instructions: types - - public void instanceOfType(Local<?> target, Local<?> source, Type<?> type) { - addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, sourcePosition, - RegisterSpecList.make(source.spec()), catches, type.constant)); - moveResult(target, true); - } - - public void typeCast(Local<?> source, Local<?> target) { - addInstruction(new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition, - RegisterSpecList.make(source.spec()), catches, target.type.constant)); - moveResult(target, true); - } - - // instructions: arrays - - public <T> void arrayLength(Local<T> array, Local<Integer> target) { - addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, sourcePosition, - RegisterSpecList.make(array.spec()), catches)); - moveResult(target, true); - } - - public <T> void newArray(Local<Integer> length, Local<T> target) { - addInstruction(new ThrowingCstInsn(Rops.opNewArray(target.type.ropType), sourcePosition, - RegisterSpecList.make(length.spec()), catches, target.type.constant)); - moveResult(target, true); - } - - public void aget(Local<?> array, Local<Integer> index, Local<?> target) { - addInstruction(new ThrowingInsn(Rops.opAget(target.type.ropType), sourcePosition, - RegisterSpecList.make(array.spec(), index.spec()), catches)); - moveResult(target, true); - } - - public void aput(Local<?> array, Local<Integer> index, Local<?> source) { - addInstruction(new ThrowingInsn(Rops.opAput(source.type.ropType), sourcePosition, - RegisterSpecList.make(source.spec(), array.spec(), index.spec()), catches)); - } - - // instructions: return - - public void returnVoid() { - if (!method.returnType.equals(Type.VOID)) { - 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()))); - } - - private void moveResult(Local<?> target, boolean afterNonInvokeThrowingInsn) { - Rop rop = afterNonInvokeThrowingInsn - ? Rops.opMoveResultPseudo(target.type.ropType) - : Rops.opMoveResult(target.type.ropType); - addInstruction(new PlainInsn(rop, sourcePosition, target.spec(), RegisterSpecList.EMPTY)); - } - - // produce BasicBlocks for dex - - BasicBlockList toBasicBlocks() { - if (!localsInitialized) { - initializeLocals(); - } - - cleanUpLabels(); - - BasicBlockList result = new BasicBlockList(labels.size()); - for (int i = 0; i < labels.size(); i++) { - result.set(i, labels.get(i).toBasicBlock()); - } - return result; - } - - /** - * Removes empty labels and assigns IDs to non-empty labels. - */ - private void cleanUpLabels() { - int id = 0; - for (Iterator<Label> i = labels.iterator(); i.hasNext();) { - Label label = i.next(); - if (label.isEmpty()) { - i.remove(); - } else { - label.compact(); - label.id = id++; - } - } - } - - private static RegisterSpecList concatenate(Local<?> first, Local<?>[] rest) { - int offset = (first != null) ? 1 : 0; - RegisterSpecList result = new RegisterSpecList(offset + rest.length); - if (first != null) { - result.set(0, first.spec()); - } - for (int i = 0; i < rest.length; i++) { - result.set(i + offset, rest[i].spec()); - } - return result; - } -} diff --git a/dx/src/com/android/dx/gen/Comparison.java b/dx/src/com/android/dx/gen/Comparison.java deleted file mode 100644 index 40a6e9724..000000000 --- a/dx/src/com/android/dx/gen/Comparison.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 com.android.dx.rop.code.Rop; -import com.android.dx.rop.code.Rops; -import com.android.dx.rop.type.TypeList; - -/** - * A comparison between two values of the same type. - */ -public enum Comparison { - - /** {@code a < b} */ - LT() { - @Override Rop rop(TypeList types) { - return Rops.opIfLt(types); - } - }, - - /** {@code a <= b} */ - LE() { - @Override Rop rop(TypeList types) { - return Rops.opIfLe(types); - } - }, - - /** {@code a == b} */ - EQ() { - @Override Rop rop(TypeList types) { - return Rops.opIfEq(types); - } - }, - - /** {@code a >= b} */ - GE() { - @Override Rop rop(TypeList types) { - return Rops.opIfGe(types); - } - }, - - /** {@code a > b} */ - GT() { - @Override Rop rop(TypeList types) { - return Rops.opIfGt(types); - } - }, - - /** {@code a != b} */ - NE() { - @Override Rop rop(TypeList types) { - return Rops.opIfNe(types); - } - }; - - abstract Rop rop(TypeList types); -} diff --git a/dx/src/com/android/dx/gen/Constants.java b/dx/src/com/android/dx/gen/Constants.java deleted file mode 100644 index 255c2e4ce..000000000 --- a/dx/src/com/android/dx/gen/Constants.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 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; - -/** - * Factory for rop constants. - */ -final class Constants { - private Constants() {} - - /** - * Returns a rop constant for the specified value. - * - * @param value null, a boxed primitive, String, Class, or Type. - */ - static TypedConstant getConstant(Object value) { - if (value == null) { - return CstKnownNull.THE_ONE; - } else if (value instanceof Boolean) { - return CstBoolean.make((Boolean) value); - } else if (value instanceof Byte) { - return CstByte.make((Byte) value); - } else if (value instanceof Character) { - return CstChar.make((Character) value); - } else if (value instanceof Double) { - return CstDouble.make(Double.doubleToLongBits((Double) value)); - } else if (value instanceof Float) { - return CstFloat.make(Float.floatToIntBits((Float) value)); - } else if (value instanceof Integer) { - return CstInteger.make((Integer) value); - } else if (value instanceof Long) { - return CstLong.make((Long) value); - } else if (value instanceof Short) { - return CstShort.make((Short) value); - } else if (value instanceof String) { - return new CstString((String) value); - } else if (value instanceof Class) { - return new CstType(Type.get((Class<?>) value).ropType); - } else if (value instanceof Type) { - return new CstType(((Type) value).ropType); - } else { - throw new UnsupportedOperationException("Not a constant: " + value); - } - } -} diff --git a/dx/src/com/android/dx/gen/DexCacheException.java b/dx/src/com/android/dx/gen/DexCacheException.java deleted file mode 100644 index 560844098..000000000 --- a/dx/src/com/android/dx/gen/DexCacheException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 java.io.IOException; - -/** Thrown when there is an IOException when writing to the dex cache directory. */ -public final class DexCacheException extends RuntimeException { - private static final long serialVersionUID = 0L; - - public DexCacheException(IOException cause) { - super(cause); - } -} diff --git a/dx/src/com/android/dx/gen/DexGenerator.java b/dx/src/com/android/dx/gen/DexGenerator.java deleted file mode 100644 index 56c516b0c..000000000 --- a/dx/src/com/android/dx/gen/DexGenerator.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * 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_PRIVATE; -import static com.android.dx.rop.code.AccessFlags.ACC_STATIC; - -import com.android.dx.dex.DexFormat; -import com.android.dx.dex.DexOptions; -import com.android.dx.dex.code.DalvCode; -import com.android.dx.dex.code.PositionList; -import com.android.dx.dex.code.RopTranslator; -import com.android.dx.dex.file.ClassDefItem; -import com.android.dx.dex.file.DexFile; -import com.android.dx.dex.file.EncodedField; -import com.android.dx.dex.file.EncodedMethod; -import com.android.dx.rop.code.AccessFlags; -import com.android.dx.rop.code.LocalVariableInfo; -import com.android.dx.rop.code.RopMethod; -import com.android.dx.rop.cst.CstString; -import com.android.dx.rop.cst.CstType; -import com.android.dx.rop.type.StdTypeList; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; - -/** - * Define types, fields and methods. - */ -public final class DexGenerator { - private final Map<Type<?>, TypeDeclaration> types - = new LinkedHashMap<Type<?>, TypeDeclaration>(); - - private TypeDeclaration getTypeDeclaration(Type<?> type) { - TypeDeclaration result = types.get(type); - if (result == null) { - result = new TypeDeclaration(type); - types.put(type, result); - } - return result; - } - - /** - * @param flags any flags masked by {@link com.android.dx.rop.code.AccessFlags#CLASS_FLAGS}. - */ - public void declare(Type<?> type, String sourceFile, int flags, - Type<?> supertype, Type<?>... interfaces) { - TypeDeclaration declaration = getTypeDeclaration(type); - if (declaration.declared) { - throw new IllegalStateException("already declared: " + type); - } - declaration.declared = true; - declaration.flags = flags; - declaration.supertype = supertype; - declaration.sourceFile = sourceFile; - declaration.interfaces = new TypeList(interfaces); - } - - /** - * @param flags any flags masked by {@link com.android.dx.rop.code.AccessFlags#METHOD_FLAGS}. - */ - public Code declare(MethodId<?, ?> method, int flags) { - TypeDeclaration typeDeclaration = getTypeDeclaration(method.declaringType); - if (typeDeclaration.methods.containsKey(method)) { - throw new IllegalStateException("already declared: " + method); - } - MethodDeclaration methodDeclaration = new MethodDeclaration(method, flags); - typeDeclaration.methods.put(method, methodDeclaration); - return methodDeclaration.code; - } - - /** - * @param flags any flags masked by {@link AccessFlags#FIELD_FLAGS}. - */ - public void declare(FieldId<?, ?> fieldId, int flags, Object staticValue) { - TypeDeclaration typeDeclaration = getTypeDeclaration(fieldId.declaringType); - if (typeDeclaration.fields.containsKey(fieldId)) { - throw new IllegalStateException("already declared: " + fieldId); - } - FieldDeclaration fieldDeclaration = new FieldDeclaration(fieldId, flags, staticValue); - typeDeclaration.fields.put(fieldId, fieldDeclaration); - } - - /** - * Returns a .dex formatted file. - */ - public byte[] generate() { - DexOptions options = new DexOptions(); - options.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; - DexFile outputDex = new DexFile(options); - - for (TypeDeclaration typeDeclaration : types.values()) { - outputDex.add(typeDeclaration.toClassDefItem()); - } - - try { - return outputDex.toDex(null, false); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Loads the generated types into the current process. - * - * <p>All parameters are optional, you may pass {@code null} and suitable - * defaults will be used. - * - * <p>If you opt to provide your own output directories, take care to - * ensure that they are not world-readable, otherwise a malicious app will - * be able to inject code to run. A suitable parameter for these output - * directories would be something like this: - * {@code getApplicationContext().getDir("dx", Context.MODE_PRIVATE); } - * - * @param parent the parent ClassLoader to be used when loading - * our generated types - * @param dexOutputDir the destination directory wherein we will write - * emitted .dex files before they end up in the cache directory - * @param dexOptCacheDir where optimized .dex files are to be written - */ - public ClassLoader load(ClassLoader parent, File dexOutputDir, File dexOptCacheDir) - throws IOException { - byte[] dex = generate(); - - /* - * This implementation currently dumps the dex to the filesystem. It - * jars the emitted .dex for the benefit of Gingerbread and earlier - * devices, which can't load .dex files directly. - * - * TODO: load the dex from memory where supported. - */ - File result = File.createTempFile("Generated", ".jar", dexOutputDir); - result.deleteOnExit(); - JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result)); - jarOut.putNextEntry(new JarEntry(DexFormat.DEX_IN_JAR_NAME)); - jarOut.write(dex); - jarOut.closeEntry(); - jarOut.close(); - try { - return (ClassLoader) Class.forName("dalvik.system.DexClassLoader") - .getConstructor(String.class, String.class, String.class, ClassLoader.class) - .newInstance(result.getPath(), dexOptCacheDir.getAbsolutePath(), null, parent); - } catch (ClassNotFoundException e) { - throw new UnsupportedOperationException("load() requires a Dalvik VM", e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e.getCause()); - } catch (InstantiationException e) { - throw new AssertionError(); - } catch (NoSuchMethodException e) { - throw new AssertionError(); - } catch (IllegalAccessException e) { - throw new AssertionError(); - } - } - - private static class TypeDeclaration { - private final Type<?> type; - - /** declared state */ - private boolean declared; - private int flags; - private Type<?> supertype; - private String sourceFile; - private TypeList interfaces; - - private final Map<FieldId, FieldDeclaration> fields - = new LinkedHashMap<FieldId, FieldDeclaration>(); - private final Map<MethodId, MethodDeclaration> methods - = new LinkedHashMap<MethodId, MethodDeclaration>(); - - TypeDeclaration(Type<?> type) { - this.type = type; - } - - ClassDefItem toClassDefItem() { - if (!declared) { - throw new IllegalStateException("Undeclared type " + type + " declares members: " - + fields.keySet() + " " + methods.keySet()); - } - - DexOptions dexOptions = new DexOptions(); - dexOptions.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; - - CstType thisType = type.constant; - - ClassDefItem out = new ClassDefItem(thisType, flags, supertype.constant, - interfaces.ropTypes, new CstString(sourceFile)); - - for (MethodDeclaration method : methods.values()) { - EncodedMethod encoded = method.toEncodedMethod(dexOptions); - if (method.isDirect()) { - out.addDirectMethod(encoded); - } else { - out.addVirtualMethod(encoded); - } - } - for (FieldDeclaration field : fields.values()) { - EncodedField encoded = field.toEncodedField(); - if (field.isStatic()) { - out.addStaticField(encoded, Constants.getConstant(field.staticValue)); - } else { - out.addInstanceField(encoded); - } - } - - return out; - } - } - - static class FieldDeclaration { - final FieldId<?, ?> fieldId; - private final int accessFlags; - private final Object staticValue; - - FieldDeclaration(FieldId<?, ?> fieldId, int accessFlags, Object staticValue) { - if ((accessFlags & (AccessFlags.ACC_STATIC)) == 0 && staticValue != null) { - throw new IllegalArgumentException("instance fields may not have a value"); - } - this.fieldId = fieldId; - this.accessFlags = accessFlags; - this.staticValue = staticValue; - } - - EncodedField toEncodedField() { - return new EncodedField(fieldId.constant, accessFlags); - } - - public boolean isStatic() { - return (accessFlags & (AccessFlags.ACC_STATIC)) != 0; - } - } - - static class MethodDeclaration { - final MethodId<?, ?> method; - private final int flags; - private final Code code; - - public MethodDeclaration(MethodId<?, ?> method, int flags) { - this.method = method; - this.flags = flags; - this.code = new Code(this); - } - - boolean isStatic() { - return (flags & ACC_STATIC) != 0; - } - - boolean isDirect() { - return (flags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) != 0; - } - - EncodedMethod toEncodedMethod(DexOptions dexOptions) { - RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0); - LocalVariableInfo locals = null; - DalvCode dalvCode = RopTranslator.translate( - ropMethod, PositionList.NONE, locals, code.paramSize(), dexOptions); - return new EncodedMethod(method.constant, flags, dalvCode, StdTypeList.EMPTY); - } - } -} diff --git a/dx/src/com/android/dx/gen/FieldId.java b/dx/src/com/android/dx/gen/FieldId.java deleted file mode 100644 index 62ef73bdd..000000000 --- a/dx/src/com/android/dx/gen/FieldId.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 com.android.dx.rop.cst.CstFieldRef; -import com.android.dx.rop.cst.CstNat; -import com.android.dx.rop.cst.CstString; - -/** - * A field. - */ -public final class FieldId<D, V> { - final Type<D> declaringType; - final Type<V> type; - final String name; - - /** cached converted state */ - final CstNat nat; - final CstFieldRef constant; - - FieldId(Type<D> declaringType, Type<V> type, String name) { - if (declaringType == null || type == null || name == null) { - throw new NullPointerException(); - } - this.declaringType = declaringType; - this.type = type; - this.name = name; - this.nat = new CstNat(new CstString(name), new CstString(type.name)); - this.constant = new CstFieldRef(declaringType.constant, nat); - } - - public Type<D> getDeclaringType() { - return declaringType; - } - - public Type<V> getType() { - return type; - } - - public String getName() { - return name; - } - - @Override public boolean equals(Object o) { - return o instanceof FieldId - && ((FieldId<?, ?>) o).declaringType.equals(declaringType) - && ((FieldId<?, ?>) o).name.equals(name); - } - - @Override public int hashCode() { - return declaringType.hashCode() + 37 * name.hashCode(); - } - - @Override public String toString() { - return declaringType + "." + name; - } -} diff --git a/dx/src/com/android/dx/gen/Label.java b/dx/src/com/android/dx/gen/Label.java deleted file mode 100644 index 633b5f161..000000000 --- a/dx/src/com/android/dx/gen/Label.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 com.android.dx.rop.code.BasicBlock; -import com.android.dx.rop.code.Insn; -import com.android.dx.rop.code.InsnList; -import com.android.dx.util.IntList; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A branch target in a list of instructions. - */ -public final class Label { - - final List<Insn> instructions = new ArrayList<Insn>(); - - boolean marked = false; - - /** an immutable list of labels corresponding to the types in the catch list */ - List<Label> catchLabels = Collections.emptyList(); - - /** contains the next instruction if no branch occurs */ - Label primarySuccessor; - - /** contains the instruction to jump to if the if is true */ - Label alternateSuccessor; - - int id = -1; - - Label() {} - - boolean isEmpty() { - return instructions.isEmpty(); - } - - void compact() { - for (int i = 0; i < catchLabels.size(); i++) { - while (catchLabels.get(i).isEmpty()) { - catchLabels.set(i, catchLabels.get(i).primarySuccessor); - } - } - while (primarySuccessor != null && primarySuccessor.isEmpty()) { - primarySuccessor = primarySuccessor.primarySuccessor; - } - while (alternateSuccessor != null && alternateSuccessor.isEmpty()) { - alternateSuccessor = alternateSuccessor.primarySuccessor; - } - } - - BasicBlock toBasicBlock() { - InsnList result = new InsnList(instructions.size()); - for (int i = 0; i < instructions.size(); i++) { - result.set(i, instructions.get(i)); - } - result.setImmutable(); - - int primarySuccessorIndex = -1; - IntList successors = new IntList(); - for (Label catchLabel : catchLabels) { - successors.add(catchLabel.id); - } - if (primarySuccessor != null) { - primarySuccessorIndex = primarySuccessor.id; - successors.add(primarySuccessorIndex); - } - if (alternateSuccessor != null) { - successors.add(alternateSuccessor.id); - } - successors.setImmutable(); - - return new BasicBlock(id, result, successors, primarySuccessorIndex); - } -} diff --git a/dx/src/com/android/dx/gen/Local.java b/dx/src/com/android/dx/gen/Local.java deleted file mode 100644 index b98759c3f..000000000 --- a/dx/src/com/android/dx/gen/Local.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 com.android.dx.rop.code.RegisterSpec; - -/** - * A temporary variable that holds a single value. - */ -public final class Local<T> { - private final Code code; - final Type<T> type; - private int reg = -1; - private RegisterSpec spec; - - private Local(Code code, Type<T> type) { - this.code = code; - this.type = type; - } - - static <T> Local<T> get(Code code, Type<T> type) { - return new Local<T>(code, type); - } - - /** - * Assigns registers to this local. - * - * @return the number of registers required. - */ - int initialize(int reg) { - this.reg = reg; - this.spec = RegisterSpec.make(reg, type.ropType); - return size(); - } - - int size() { - return type.ropType.getCategory(); - } - - RegisterSpec spec() { - if (spec == null) { - code.initializeLocals(); - if (spec == null) { - throw new AssertionError(); - } - } - return spec; - } - - public Type getType() { - return type; - } - - @Override public String toString() { - return "v" + reg + "(" + type + ")"; - } -} diff --git a/dx/src/com/android/dx/gen/MethodId.java b/dx/src/com/android/dx/gen/MethodId.java deleted file mode 100644 index 29d088ab4..000000000 --- a/dx/src/com/android/dx/gen/MethodId.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 com.android.dx.rop.cst.CstMethodRef; -import com.android.dx.rop.cst.CstNat; -import com.android.dx.rop.cst.CstString; -import com.android.dx.rop.type.Prototype; -import java.util.List; - -/** - * A method or constructor. - */ -public final class MethodId<D, R> { - final Type<D> declaringType; - final Type<R> returnType; - final String name; - final TypeList parameters; - - /** cached converted state */ - final CstNat nat; - final CstMethodRef constant; - - MethodId(Type<D> declaringType, Type<R> returnType, String name, TypeList parameters) { - if (declaringType == null || returnType == null || name == null || parameters == null) { - throw new NullPointerException(); - } - this.declaringType = declaringType; - this.returnType = returnType; - this.name = name; - this.parameters = parameters; - this.nat = new CstNat(new CstString(name), new CstString(descriptor(false))); - this.constant = new CstMethodRef(declaringType.constant, nat); - } - - public Type<D> getDeclaringType() { - return declaringType; - } - - public Type<R> getReturnType() { - return returnType; - } - - public String getName() { - return name; - } - - public List<Type<?>> getParameters() { - return parameters.asList(); - } - - /** - * Returns a descriptor like "(Ljava/lang/Class;[I)Ljava/lang/Object;". - */ - String descriptor(boolean includeThis) { - StringBuilder result = new StringBuilder(); - result.append("("); - if (includeThis) { - result.append(declaringType.name); - } - for (Type t : parameters.types) { - result.append(t.name); - } - result.append(")"); - result.append(returnType.name); - return result.toString(); - } - - Prototype prototype(boolean includeThis) { - return Prototype.intern(descriptor(includeThis)); - } - - @Override public boolean equals(Object o) { - return o instanceof MethodId - && ((MethodId<?, ?>) o).declaringType.equals(declaringType) - && ((MethodId<?, ?>) o).name.equals(name) - && ((MethodId<?, ?>) o).parameters.equals(parameters) - && ((MethodId<?, ?>) o).returnType.equals(returnType); - } - - @Override public int hashCode() { - int result = 17; - result = 31 * result + declaringType.hashCode(); - result = 31 * result + name.hashCode(); - result = 31 * result + parameters.hashCode(); - result = 31 * result + returnType.hashCode(); - return result; - } - - @Override public String toString() { - return declaringType + "." + name + "(" + parameters + ")"; - } -} diff --git a/dx/src/com/android/dx/gen/ProxyBuilder.java b/dx/src/com/android/dx/gen/ProxyBuilder.java deleted file mode 100644 index 852fe666f..000000000 --- a/dx/src/com/android/dx/gen/ProxyBuilder.java +++ /dev/null @@ -1,667 +0,0 @@ -/* - * 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_PRIVATE; -import static com.android.dx.rop.code.AccessFlags.ACC_PUBLIC; -import static com.android.dx.rop.code.AccessFlags.ACC_STATIC; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Creates dynamic proxies of concrete classes. - * <p> - * This is similar to the {@code java.lang.reflect.Proxy} class, but works for classes instead of - * interfaces. - * <h3>Example</h3> - * The following example demonstrates the creation of a dynamic proxy for {@code java.util.Random} - * which will always return 4 when asked for integers, and which logs method calls to every method. - * <pre> - * InvocationHandler handler = new InvocationHandler() { - * @Override - * public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - * if (method.getName().equals("nextInt")) { - * // Chosen by fair dice roll, guaranteed to be random. - * return 4; - * } - * Object result = ProxyBuilder.callSuper(proxy, method, args); - * System.out.println("Method: " + method.getName() + " args: " - * + Arrays.toString(args) + " result: " + result); - * return result; - * } - * }; - * Random debugRandom = ProxyBuilder.forClass(Random.class) - * .dexCache(getInstrumentation().getTargetContext().getDir("dx", Context.MODE_PRIVATE)) - * .handler(handler) - * .build(); - * assertEquals(4, debugRandom.nextInt()); - * debugRandom.setSeed(0); - * assertTrue(debugRandom.nextBoolean()); - * </pre> - * <h3>Usage</h3> - * Call {@link #forClass(Class)} for the Class you wish to proxy. Call - * {@link #handler(InvocationHandler)} passing in an {@link InvocationHandler}, and then call - * {@link #build()}. The returned instance will be a dynamically generated subclass where all method - * calls will be delegated to the invocation handler, except as noted below. - * <p> - * The static method {@link #callSuper(Object, Method, Object...)} allows you to access the original - * super method for a given proxy. This allows the invocation handler to selectively override some - * methods but not others. - * <p> - * By default, the {@link #build()} method will call the no-arg constructor belonging to the class - * being proxied. If you wish to call a different constructor, you must provide arguments for both - * {@link #constructorArgTypes(Class[])} and {@link #constructorArgValues(Object[])}. - * <p> - * This process works only for classes with public and protected level of visibility. - * <p> - * You may proxy abstract classes. You may not proxy final classes. - * <p> - * Only non-private, non-final, non-static methods will be dispatched to the invocation handler. - * Private, static or final methods will always call through to the superclass as normal. - * <p> - * The {@link #finalize()} method on {@code Object} will not be proxied. - * <p> - * You must provide a dex cache directory via the {@link #dexCache(File)} method. You should take - * care not to make this a world-writable directory, so that third parties cannot inject code into - * your application. A suitable parameter for these output directories would be something like - * this: - * <pre>{@code - * getApplicationContext().getDir("dx", Context.MODE_PRIVATE); - * }</pre> - * <p> - * If the base class to be proxied leaks the {@code this} pointer in the constructor (bad practice), - * that is to say calls a non-private non-final method from the constructor, the invocation handler - * will not be invoked. As a simple concrete example, when proxying Random we discover that it - * inernally calls setSeed during the constructor. The proxy will not intercept this call during - * proxy construction, but will intercept as normal afterwards. This behaviour may be subject to - * change in future releases. - * <p> - * This class is <b>not thread safe</b>. - */ -public final class ProxyBuilder<T> { - private static final String FIELD_NAME_HANDLER = "$__handler"; - private static final String FIELD_NAME_METHODS = "$__methodArray"; - - private final Class<T> baseClass; - private ClassLoader parentClassLoader = ProxyBuilder.class.getClassLoader(); - private InvocationHandler handler; - private File dexCache; - private Class<?>[] constructorArgTypes = new Class[0]; - private Object[] constructorArgValues = new Object[0]; - - private ProxyBuilder(Class<T> clazz) { - baseClass = clazz; - } - - public static <T> ProxyBuilder<T> forClass(Class<T> clazz) { - return new ProxyBuilder<T>(clazz); - } - - /** - * Specifies the parent ClassLoader to use when creating the proxy. - * - * <p>If null, {@code ProxyBuilder.class.getClassLoader()} will be used. - */ - public ProxyBuilder<T> parentClassLoader(ClassLoader parent) { - parentClassLoader = parent; - return this; - } - - public ProxyBuilder<T> handler(InvocationHandler handler) { - this.handler = handler; - return this; - } - - public ProxyBuilder<T> dexCache(File dexCache) { - this.dexCache = dexCache; - return this; - } - - public ProxyBuilder<T> constructorArgValues(Object... constructorArgValues) { - this.constructorArgValues = constructorArgValues; - return this; - } - - public ProxyBuilder<T> constructorArgTypes(Class<?>... constructorArgTypes) { - this.constructorArgTypes = constructorArgTypes; - return this; - } - - /** - * Create a new instance of the class to proxy. - * - * @throws UnsupportedOperationException if the class we are trying to create a proxy for is - * not accessible. - * @throws DexCacheException if an exception occurred writing to the {@code dexCache} directory. - * @throws UndeclaredThrowableException if the constructor for the base class to proxy throws - * a declared exception during construction. - * @throws IllegalArgumentException if the handler is null, if the constructor argument types - * do not match the constructor argument values, or if no such constructor exists. - */ - public T build() { - check(handler != null, "handler == null"); - check(constructorArgTypes.length == constructorArgValues.length, - "constructorArgValues.length != constructorArgTypes.length"); - DexGenerator generator = new DexGenerator(); - String generatedName = getMethodNameForProxyOf(baseClass); - Type<? extends T> generatedType = Type.get("L" + generatedName + ";"); - Type<T> superType = Type.get(baseClass); - generateConstructorsAndFields(generator, generatedType, superType, baseClass); - Method[] methodsToProxy = getMethodsToProxy(baseClass); - generateCodeForAllMethods(generator, generatedType, methodsToProxy, superType); - generator.declare(generatedType, generatedName + ".generated", ACC_PUBLIC, superType); - ClassLoader classLoader; - try { - classLoader = generator.load(parentClassLoader, dexCache, dexCache); - } catch (IOException e) { - throw new DexCacheException(e); - } - Class<? extends T> proxyClass; - try { - proxyClass = loadClass(classLoader, generatedName); - } catch (IllegalAccessError e) { - // Thrown when the base class is not accessible. - throw new UnsupportedOperationException("cannot proxy inaccessible classes", e); - } catch (ClassNotFoundException e) { - // Should not be thrown, we're sure to have generated this class. - throw new AssertionError(e); - } - setMethodsStaticField(proxyClass, methodsToProxy); - Constructor<? extends T> constructor; - try { - constructor = proxyClass.getConstructor(constructorArgTypes); - } catch (NoSuchMethodException e) { - // Thrown when the ctor to be called does not exist. - throw new IllegalArgumentException("could not find matching constructor", e); - } - T result; - try { - result = constructor.newInstance(constructorArgValues); - } catch (InstantiationException e) { - // Should not be thrown, generated class is not abstract. - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // Should not be thrown, the generated constructor is accessible. - throw new AssertionError(e); - } catch (InvocationTargetException e) { - // Thrown when the base class ctor throws an exception. - throw launderCause(e); - } - setHandlerInstanceField(result, handler); - return result; - } - - // The type cast is safe: the generated type will extend the base class type. - @SuppressWarnings("unchecked") - private Class<? extends T> loadClass(ClassLoader classLoader, String generatedName) - throws ClassNotFoundException { - return (Class<? extends T>) classLoader.loadClass(generatedName); - } - - private static RuntimeException launderCause(InvocationTargetException e) { - Throwable cause = e.getCause(); - // Errors should be thrown as they are. - if (cause instanceof Error) { - throw (Error) cause; - } - // RuntimeException can be thrown as-is. - if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } - // Declared exceptions will have to be wrapped. - throw new UndeclaredThrowableException(cause); - } - - private static void setHandlerInstanceField(Object instance, InvocationHandler handler) { - try { - Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); - handlerField.setAccessible(true); - handlerField.set(instance, handler); - } catch (NoSuchFieldException e) { - // Should not be thrown, generated proxy class has been generated with this field. - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // Should not be thrown, we just set the field to accessible. - throw new AssertionError(e); - } - } - - private static void setMethodsStaticField(Class<?> proxyClass, Method[] methodsToProxy) { - try { - Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS); - methodArrayField.setAccessible(true); - methodArrayField.set(null, methodsToProxy); - } catch (NoSuchFieldException e) { - // Should not be thrown, generated proxy class has been generated with this field. - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // Should not be thrown, we just set the field to accessible. - throw new AssertionError(e); - } - } - - /** - * Returns the proxy's {@link InvocationHandler}. - * - * @throws IllegalArgumentException if the object supplied is not a proxy created by this class. - */ - public static InvocationHandler getInvocationHandler(Object instance) { - try { - Field field = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); - field.setAccessible(true); - return (InvocationHandler) field.get(instance); - } catch (NoSuchFieldException e) { - throw new IllegalArgumentException("Not a valid proxy instance", e); - } catch (IllegalAccessException e) { - // Should not be thrown, we just set the field to accessible. - throw new AssertionError(e); - } - } - - private static <T, G extends T> void generateCodeForAllMethods(DexGenerator generator, - Type<G> generatedType, Method[] methodsToProxy, Type<T> superclassType) { - Type<InvocationHandler> handlerType = Type.get(InvocationHandler.class); - Type<Method[]> methodArrayType = Type.get(Method[].class); - FieldId<G, InvocationHandler> handlerField = - generatedType.getField(handlerType, FIELD_NAME_HANDLER); - FieldId<G, Method[]> allMethods = - generatedType.getField(methodArrayType, FIELD_NAME_METHODS); - Type<Method> methodType = Type.get(Method.class); - Type<Object[]> objectArrayType = Type.get(Object[].class); - MethodId<InvocationHandler, Object> methodInvoke = handlerType.getMethod(Type.OBJECT, - "invoke", Type.OBJECT, methodType, objectArrayType); - for (int m = 0; m < methodsToProxy.length; ++m) { - /* - * If the 5th method on the superclass Example that can be overridden were to look like - * this: - * - * public int doSomething(Bar param0, int param1) { - * ... - * } - * - * Then the following code will generate a method on the proxy that looks something - * like this: - * - * public int doSomething(Bar param0, int param1) { - * int methodIndex = 4; - * Method[] allMethods = Example_Proxy.$__methodArray; - * Method thisMethod = allMethods[methodIndex]; - * int argsLength = 2; - * Object[] args = new Object[argsLength]; - * InvocationHandler localHandler = this.$__handler; - * // for-loop begins - * int p = 0; - * Bar parameter0 = param0; - * args[p] = parameter0; - * p = 1; - * int parameter1 = param1; - * Integer boxed1 = Integer.valueOf(parameter1); - * args[p] = boxed1; - * // for-loop ends - * Object result = localHandler.invoke(this, thisMethod, args); - * Integer castResult = (Integer) result; - * int unboxedResult = castResult.intValue(); - * return unboxedResult; - * } - * - * Or, in more idiomatic Java: - * - * public int doSomething(Bar param0, int param1) { - * if ($__handler == null) { - * return super.doSomething(param0, param1); - * } - * return __handler.invoke(this, __methodArray[4], - * new Object[] { param0, Integer.valueOf(param1) }); - * } - */ - Method method = methodsToProxy[m]; - String name = method.getName(); - Class<?>[] argClasses = method.getParameterTypes(); - Type<?>[] argTypes = new Type<?>[argClasses.length]; - for (int i = 0; i < argTypes.length; ++i) { - argTypes[i] = Type.get(argClasses[i]); - } - Class<?> returnType = method.getReturnType(); - Type<?> resultType = Type.get(returnType); - MethodId<T, ?> superMethod = superclassType.getMethod(resultType, name, argTypes); - MethodId<?, ?> methodId = generatedType.getMethod(resultType, name, argTypes); - Code code = generator.declare(methodId, ACC_PUBLIC); - Local<G> localThis = code.getThis(generatedType); - Local<InvocationHandler> localHandler = code.newLocal(handlerType); - Local<Object> invokeResult = code.newLocal(Type.OBJECT); - Local<Integer> intValue = code.newLocal(Type.INT); - Local<Object[]> args = code.newLocal(objectArrayType); - Local<Integer> argsLength = code.newLocal(Type.INT); - Local<Object> temp = code.newLocal(Type.OBJECT); - Local<?> resultHolder = code.newLocal(resultType); - Local<Method[]> methodArray = code.newLocal(methodArrayType); - Local<Method> thisMethod = code.newLocal(methodType); - Local<Integer> methodIndex = code.newLocal(Type.INT); - Class<?> aBoxedClass = PRIMITIVE_TO_BOXED.get(returnType); - Local<?> aBoxedResult = null; - if (aBoxedClass != null) { - aBoxedResult = code.newLocal(Type.get(aBoxedClass)); - } - Local<?>[] superArgs2 = new Local<?>[argClasses.length]; - Local<?> superResult2 = code.newLocal(resultType); - Local<InvocationHandler> nullHandler = code.newLocal(handlerType); - - code.loadConstant(methodIndex, m); - code.sget(allMethods, methodArray); - code.aget(methodArray, methodIndex, thisMethod); - code.loadConstant(argsLength, argTypes.length); - code.newArray(argsLength, args); - code.iget(handlerField, localThis, localHandler); - - // if (proxy == null) - code.loadConstant(nullHandler, null); - Label handlerNullCase = code.newLabel(); - code.compare(Comparison.EQ, nullHandler, localHandler, handlerNullCase); - - // This code is what we execute when we have a valid proxy: delegate to invocation - // handler. - for (int p = 0; p < argTypes.length; ++p) { - code.loadConstant(intValue, p); - Local<?> parameter = code.getParameter(p, argTypes[p]); - Local<?> unboxedIfNecessary = boxIfRequired(generator, code, parameter, temp); - code.aput(args, intValue, unboxedIfNecessary); - } - code.invokeInterface(methodInvoke, invokeResult, localHandler, - localThis, thisMethod, args); - generateCodeForReturnStatement(code, returnType, invokeResult, resultHolder, - aBoxedResult); - - // This code is executed if proxy is null: call the original super method. - // This is required to handle the case of construction of an object which leaks the - // "this" pointer. - code.mark(handlerNullCase); - for (int i = 0; i < superArgs2.length; ++i) { - superArgs2[i] = code.getParameter(i, argTypes[i]); - } - if (void.class.equals(returnType)) { - code.invokeSuper(superMethod, null, localThis, superArgs2); - code.returnVoid(); - } else { - invokeSuper(superMethod, code, localThis, superArgs2, superResult2); - code.returnValue(superResult2); - } - - /* - * And to allow calling the original super method, the following is also generated: - * - * public int super_doSomething(Bar param0, int param1) { - * int result = super.doSomething(param0, param1); - * return result; - * } - */ - String superName = "super_" + name; - MethodId<G, ?> callsSuperMethod = generatedType.getMethod( - resultType, superName, argTypes); - Code superCode = generator.declare(callsSuperMethod, ACC_PUBLIC); - Local<G> superThis = superCode.getThis(generatedType); - Local<?>[] superArgs = new Local<?>[argClasses.length]; - for (int i = 0; i < superArgs.length; ++i) { - superArgs[i] = superCode.getParameter(i, argTypes[i]); - } - if (void.class.equals(returnType)) { - superCode.invokeSuper(superMethod, null, superThis, superArgs); - superCode.returnVoid(); - } else { - Local<?> superResult = superCode.newLocal(resultType); - invokeSuper(superMethod, superCode, superThis, superArgs, superResult); - superCode.returnValue(superResult); - } - } - } - - // This one is tricky to fix, I gave up. - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static <T> void invokeSuper(MethodId superMethod, Code superCode, - Local superThis, Local[] superArgs, Local superResult) { - superCode.invokeSuper(superMethod, superResult, superThis, superArgs); - } - - private static Local<?> boxIfRequired(DexGenerator generator, Code code, Local<?> parameter, - Local<Object> temp) { - MethodId<?, ?> unboxMethod = PRIMITIVE_TYPE_TO_UNBOX_METHOD.get(parameter.getType()); - if (unboxMethod == null) { - return parameter; - } - code.invokeStatic(unboxMethod, temp, parameter); - return temp; - } - - public static Object callSuper(Object proxy, Method method, Object... args) - throws SecurityException, IllegalAccessException, - InvocationTargetException, NoSuchMethodException { - return proxy.getClass() - .getMethod("super_" + method.getName(), method.getParameterTypes()) - .invoke(proxy, args); - } - - private static void check(boolean condition, String message) { - if (!condition) { - throw new IllegalArgumentException(message); - } - } - - private static <T, G extends T> void generateConstructorsAndFields(DexGenerator generator, - Type<G> generatedType, Type<T> superType, Class<T> superClass) { - Type<InvocationHandler> handlerType = Type.get(InvocationHandler.class); - Type<Method[]> methodArrayType = Type.get(Method[].class); - FieldId<G, InvocationHandler> handlerField = generatedType.getField( - handlerType, FIELD_NAME_HANDLER); - generator.declare(handlerField, ACC_PRIVATE, null); - FieldId<G, Method[]> allMethods = generatedType.getField( - methodArrayType, FIELD_NAME_METHODS); - generator.declare(allMethods, ACC_PRIVATE | ACC_STATIC, null); - for (Constructor<T> constructor : getConstructorsToOverwrite(superClass)) { - if (constructor.getModifiers() == Modifier.FINAL) { - continue; - } - Type<?>[] types = classArrayToTypeArray(constructor.getParameterTypes()); - MethodId<?, ?> method = generatedType.getConstructor(types); - Code constructorCode = generator.declare(method, ACC_PUBLIC | ACC_CONSTRUCTOR); - Local<G> thisRef = constructorCode.getThis(generatedType); - Local<?>[] params = new Local[types.length]; - for (int i = 0; i < params.length; ++i) { - params[i] = constructorCode.getParameter(i, types[i]); - } - MethodId<T, ?> superConstructor = superType.getConstructor(types); - constructorCode.invokeDirect(superConstructor, null, thisRef, params); - constructorCode.returnVoid(); - } - } - - // The type parameter on Constructor is the class in which the constructor is declared. - // The getDeclaredConstructors() method gets constructors declared only in the given class, - // hence this cast is safe. - @SuppressWarnings("unchecked") - private static <T> Constructor<T>[] getConstructorsToOverwrite(Class<T> clazz) { - return (Constructor<T>[]) clazz.getDeclaredConstructors(); - } - - /** - * Gets all {@link Method} objects we can proxy in the hierarchy of the supplied class. - */ - private static <T> Method[] getMethodsToProxy(Class<T> clazz) { - Set<MethodSetEntry> methodsToProxy = new HashSet<MethodSetEntry>(); - for (Class<?> current = clazz; current != null; current = current.getSuperclass()) { - for (Method method : current.getDeclaredMethods()) { - if ((method.getModifiers() & Modifier.FINAL) != 0) { - // Skip final methods, we can't override them. - continue; - } - if ((method.getModifiers() & Modifier.STATIC) != 0) { - // Skip static methods, overriding them has no effect. - continue; - } - if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) { - // Skip finalize method, it's likely important that it execute as normal. - continue; - } - methodsToProxy.add(new MethodSetEntry(method)); - } - } - Method[] results = new Method[methodsToProxy.size()]; - int i = 0; - for (MethodSetEntry entry : methodsToProxy) { - results[i++] = entry.originalMethod; - } - return results; - } - - private static <T> String getMethodNameForProxyOf(Class<T> clazz) { - return clazz.getSimpleName() + "_Proxy"; - } - - private static Type<?>[] classArrayToTypeArray(Class<?>[] input) { - Type<?>[] result = new Type[input.length]; - for (int i = 0; i < input.length; ++i) { - result[i] = Type.get(input[i]); - } - return result; - } - - /** - * Calculates the correct return statement code for a method. - * <p> - * A void method will not return anything. A method that returns a primitive will need to - * unbox the boxed result. Otherwise we will cast the result. - */ - // This one is tricky to fix, I gave up. - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static void generateCodeForReturnStatement(Code code, Class methodReturnType, - Local localForResultOfInvoke, Local localOfMethodReturnType, Local aBoxedResult) { - if (PRIMITIVE_TO_UNBOX_METHOD.containsKey(methodReturnType)) { - code.typeCast(localForResultOfInvoke, aBoxedResult); - MethodId unboxingMethodFor = getUnboxMethodForPrimitive(methodReturnType); - code.invokeVirtual(unboxingMethodFor, localOfMethodReturnType, aBoxedResult); - code.returnValue(localOfMethodReturnType); - } else if (void.class.equals(methodReturnType)) { - code.returnVoid(); - } else { - code.typeCast(localForResultOfInvoke, localOfMethodReturnType); - code.returnValue(localOfMethodReturnType); - } - } - - private static MethodId<?, ?> getUnboxMethodForPrimitive(Class<?> methodReturnType) { - return PRIMITIVE_TO_UNBOX_METHOD.get(methodReturnType); - } - - private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED; - static { - PRIMITIVE_TO_BOXED = new HashMap<Class<?>, Class<?>>(); - PRIMITIVE_TO_BOXED.put(boolean.class, Boolean.class); - PRIMITIVE_TO_BOXED.put(int.class, Integer.class); - PRIMITIVE_TO_BOXED.put(byte.class, Byte.class); - PRIMITIVE_TO_BOXED.put(long.class, Long.class); - PRIMITIVE_TO_BOXED.put(short.class, Short.class); - PRIMITIVE_TO_BOXED.put(float.class, Float.class); - PRIMITIVE_TO_BOXED.put(double.class, Double.class); - PRIMITIVE_TO_BOXED.put(char.class, Character.class); - } - - private static final Map<Type<?>, MethodId<?, ?>> PRIMITIVE_TYPE_TO_UNBOX_METHOD; - static { - PRIMITIVE_TYPE_TO_UNBOX_METHOD = new HashMap<Type<?>, MethodId<?, ?>>(); - for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_TO_BOXED.entrySet()) { - Type<?> primitiveType = Type.get(entry.getKey()); - Type<?> boxedType = Type.get(entry.getValue()); - MethodId<?, ?> valueOfMethod = boxedType.getMethod(boxedType, "valueOf", primitiveType); - PRIMITIVE_TYPE_TO_UNBOX_METHOD.put(primitiveType, valueOfMethod); - } - } - - /** - * Map from primitive type to method used to unbox a boxed version of the primitive. - * <p> - * This is required for methods whose return type is primitive, since the - * {@link InvocationHandler} will return us a boxed result, and we'll need to convert it back to - * primitive value. - */ - private static final Map<Class<?>, MethodId<?, ?>> PRIMITIVE_TO_UNBOX_METHOD; - static { - Map<Class<?>, MethodId<?, ?>> map = new HashMap<Class<?>, MethodId<?, ?>>(); - map.put(boolean.class, Type.get(Boolean.class).getMethod(Type.BOOLEAN, "booleanValue")); - map.put(int.class, Type.get(Integer.class).getMethod(Type.INT, "intValue")); - map.put(byte.class, Type.get(Byte.class).getMethod(Type.BYTE, "byteValue")); - map.put(long.class, Type.get(Long.class).getMethod(Type.LONG, "longValue")); - map.put(short.class, Type.get(Short.class).getMethod(Type.SHORT, "shortValue")); - map.put(float.class, Type.get(Float.class).getMethod(Type.FLOAT, "floatValue")); - map.put(double.class, Type.get(Double.class).getMethod(Type.DOUBLE, "doubleValue")); - map.put(char.class, Type.get(Character.class).getMethod(Type.CHAR, "charValue")); - PRIMITIVE_TO_UNBOX_METHOD = map; - } - - /** - * Wrapper class to let us disambiguate {@link Method} objects. - * <p> - * The purpose of this class is to override the {@link #equals(Object)} and {@link #hashCode()} - * methods so we can use a {@link Set} to remove duplicate methods that are overrides of one - * another. For these purposes, we consider two methods to be equal if they have the same - * name, return type, and parameter types. - */ - private static class MethodSetEntry { - private final String name; - private final Class<?>[] paramTypes; - private final Class<?> returnType; - private final Method originalMethod; - - public MethodSetEntry(Method method) { - originalMethod = method; - name = method.getName(); - paramTypes = method.getParameterTypes(); - returnType = method.getReturnType(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof MethodSetEntry) { - MethodSetEntry other = (MethodSetEntry) o; - return name.equals(other.name) - && returnType.equals(other.returnType) - && Arrays.equals(paramTypes, other.paramTypes); - } - return false; - } - - @Override - public int hashCode() { - int result = 17; - result += 31 * result + name.hashCode(); - result += 31 * result + returnType.hashCode(); - result += 31 * result + Arrays.hashCode(paramTypes); - return result; - } - } -} diff --git a/dx/src/com/android/dx/gen/Type.java b/dx/src/com/android/dx/gen/Type.java deleted file mode 100644 index 3b81f6d1d..000000000 --- a/dx/src/com/android/dx/gen/Type.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 com.android.dx.rop.cst.CstType; -import java.util.HashMap; -import java.util.Map; - -/** - * A primitive type, interface or class. - */ -public final class Type<T> { - /** The {@code boolean} primitive type. */ - public static final Type<Boolean> BOOLEAN - = new Type<Boolean>(com.android.dx.rop.type.Type.BOOLEAN); - - /** The {@code byte} primitive type. */ - public static final Type<Byte> BYTE = new Type<Byte>(com.android.dx.rop.type.Type.BYTE); - - /** The {@code char} primitive type. */ - public static final Type<Character> CHAR - = new Type<Character>(com.android.dx.rop.type.Type.CHAR); - - /** The {@code double} primitive type. */ - public static final Type<Double> DOUBLE = new Type<Double>(com.android.dx.rop.type.Type.DOUBLE); - - /** The {@code float} primitive type. */ - public static final Type<Float> FLOAT = new Type<Float>(com.android.dx.rop.type.Type.FLOAT); - - /** The {@code int} primitive type. */ - public static final Type<Integer> INT = new Type<Integer>(com.android.dx.rop.type.Type.INT); - - /** The {@code long} primitive type. */ - public static final Type<Long> LONG = new Type<Long>(com.android.dx.rop.type.Type.LONG); - - /** The {@code short} primitive type. */ - public static final Type<Short> SHORT = new Type<Short>(com.android.dx.rop.type.Type.SHORT); - - /** The {@code void} primitive type. Only used as a return type. */ - public static final Type<Void> VOID = new Type<Void>(com.android.dx.rop.type.Type.VOID); - - /** The {@code Object} type. */ - public static final Type<Object> OBJECT = new Type<Object>(com.android.dx.rop.type.Type.OBJECT); - - /** The {@code String} type. */ - public static final Type<String> STRING = new Type<String>(com.android.dx.rop.type.Type.STRING); - - private static final Map<Class<?>, Type<?>> PRIMITIVE_TO_TYPE - = new HashMap<Class<?>, Type<?>>(); - static { - PRIMITIVE_TO_TYPE.put(boolean.class, BOOLEAN); - PRIMITIVE_TO_TYPE.put(byte.class, BYTE); - PRIMITIVE_TO_TYPE.put(char.class, CHAR); - PRIMITIVE_TO_TYPE.put(double.class, DOUBLE); - PRIMITIVE_TO_TYPE.put(float.class, FLOAT); - PRIMITIVE_TO_TYPE.put(int.class, INT); - PRIMITIVE_TO_TYPE.put(long.class, LONG); - PRIMITIVE_TO_TYPE.put(short.class, SHORT); - PRIMITIVE_TO_TYPE.put(void.class, VOID); - } - - final String name; - - /** cached converted values */ - final com.android.dx.rop.type.Type ropType; - final CstType constant; - - Type(com.android.dx.rop.type.Type ropType) { - this(ropType.getDescriptor(), ropType); - } - - Type(String name, com.android.dx.rop.type.Type ropType) { - if (name == null || ropType == null) { - throw new NullPointerException(); - } - this.name = name; - this.ropType = ropType; - this.constant = CstType.intern(ropType); - } - - /** - * @param name a descriptor like "Ljava/lang/Class;". - */ - public static <T> Type<T> get(String name) { - return new Type<T>(name, com.android.dx.rop.type.Type.internReturnType(name)); - } - - public static <T> Type<T> get(Class<T> type) { - if (type.isPrimitive()) { - @SuppressWarnings("unchecked") // guarded by equals - Type<T> result = (Type<T>) PRIMITIVE_TO_TYPE.get(type); - return result; - } - String name = type.getName().replace('.', '/'); - return get(type.isArray() ? name : 'L' + name + ';'); - } - - public <V> FieldId<T, V> getField(Type<V> type, String name) { - return new FieldId<T, V>(this, type, name); - } - - public MethodId<T, Void> getConstructor(Type<?>... parameters) { - return new MethodId<T, Void>(this, VOID, "<init>", new TypeList(parameters)); - } - - public <R> MethodId<T, R> getMethod(Type<R> returnType, String name, Type<?>... parameters) { - return new MethodId<T, R>(this, returnType, name, new TypeList(parameters)); - } - - public String getName() { - return name; - } - - @Override public boolean equals(Object o) { - return o instanceof Type - && ((Type) o).name.equals(name); - } - - @Override public int hashCode() { - return name.hashCode(); - } - - @Override public String toString() { - return name; - } -} diff --git a/dx/src/com/android/dx/gen/TypeList.java b/dx/src/com/android/dx/gen/TypeList.java deleted file mode 100644 index e18ed4a28..000000000 --- a/dx/src/com/android/dx/gen/TypeList.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 com.android.dx.rop.type.StdTypeList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * An immutable of types. - */ -final class TypeList { - final Type<?>[] types; - final StdTypeList ropTypes; - - TypeList(Type<?>[] types) { - this.types = types.clone(); - this.ropTypes = new StdTypeList(types.length); - for (int i = 0; i < types.length; i++) { - ropTypes.set(i, types[i].ropType); - } - } - - /** - * Returns an immutable list. - */ - public List<Type<?>> asList() { - return Collections.unmodifiableList(Arrays.asList(types)); - } - - @Override public boolean equals(Object o) { - return o instanceof TypeList && Arrays.equals(((TypeList) o).types, types); - } - - @Override public int hashCode() { - return Arrays.hashCode(types); - } - - @Override public String toString() { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < types.length; i++) { - if (i > 0) { - result.append(", "); - } - result.append(types[i]); - } - return result.toString(); - } -} |