/* Copyright (c) 2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. There is NO WARRANTY for this software. See license.txt for details. */ package java.lang.reflect; import static avian.Stream.write1; import static avian.Stream.write2; import static avian.Stream.write4; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.io.OutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class Proxy { private static final int CONSTANT_Integer = 3; private static final int CONSTANT_Utf8 = 1; private static final int CONSTANT_Class = 7; private static final int CONSTANT_NameAndType = 12; private static final int CONSTANT_Fieldref = 9; private static final int CONSTANT_Methodref = 10; private static final int aaload = 0x32; private static final int aastore = 0x53; private static final int aload = 0x19; private static final int aload_0 = 0x2a; private static final int aload_1 = 0x2b; private static final int anewarray = 0xbd; private static final int areturn = 0xb0; private static final int dload = 0x18; private static final int dreturn = 0xaf; private static final int dup = 0x59; private static final int fload = 0x17; private static final int freturn = 0xae; private static final int getfield = 0xb4; private static final int iload = 0x15; private static final int invokeinterface = 0xb9; private static final int invokestatic = 0xb8; private static final int invokevirtual = 0xb6; private static final int ireturn = 0xac; private static final int ldc_w = 0x13; private static final int lload = 0x16; private static final int lreturn = 0xad; private static final int pop = 0x57; private static final int putfield = 0xb5; private static final int return_ = 0xb1; private static int nextNumber; protected InvocationHandler h; public static Class getProxyClass(ClassLoader loader, Class ... interfaces) { for (Class c: interfaces) { if (! c.isInterface()) { throw new IllegalArgumentException(); } } int number; synchronized (Proxy.class) { number = nextNumber++; } try { return makeClass(loader, interfaces, "Proxy-" + number); } catch (IOException e) { AssertionError error = new AssertionError(); error.initCause(e); throw error; } } private static void set4(byte[] array, int offset, int v) { array[offset ] = (byte) ((v >>> 24) & 0xFF); array[offset + 1] = (byte) ((v >>> 16) & 0xFF); array[offset + 2] = (byte) ((v >>> 8) & 0xFF); array[offset + 3] = (byte) ((v ) & 0xFF); } private static int poolAdd(List pool, PoolEntry e) { int i = 0; for (PoolEntry existing: pool) { if (existing.equals(e)) { return i; } else { ++i; } } pool.add(e); return pool.size() - 1; } private static int poolAddInteger(List pool, int value) { return poolAdd(pool, new IntegerPoolEntry(value)); } private static int poolAddUtf8(List pool, String value) { return poolAdd(pool, new Utf8PoolEntry(value)); } private static int poolAddClass(List pool, String name) { return poolAdd(pool, new ClassPoolEntry(poolAddUtf8(pool, name))); } private static int poolAddNameAndType(List pool, String name, String type) { return poolAdd(pool, new NameAndTypePoolEntry (poolAddUtf8(pool, name), poolAddUtf8(pool, type))); } private static int poolAddFieldRef(List pool, String className, String name, String spec) { return poolAdd(pool, new FieldRefPoolEntry (poolAddClass(pool, className), poolAddNameAndType(pool, name, spec))); } private static int poolAddMethodRef(List pool, String className, String name, String spec) { return poolAdd(pool, new MethodRefPoolEntry (poolAddClass(pool, className), poolAddNameAndType(pool, name, spec))); } private static byte[] makeInvokeCode(List pool, String className, byte[] spec, int parameterCount, int parameterFootprint, int index) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); write2(out, 8); // max stack write2(out, parameterFootprint); // max locals write4(out, 0); // length (we'll set the real value later) write1(out, aload_0); write1(out, getfield); write2(out, poolAddFieldRef (pool, "java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;") + 1); write1(out, aload_0); write1(out, ldc_w); write2(out, poolAddClass(pool, className) + 1); write1(out, getfield); write2(out, poolAddFieldRef (pool, "java/lang/Class", "methodTable", "[Ljava/lang/reflect/Method;") + 1); write1(out, ldc_w); write2(out, poolAddInteger(pool, index) + 1); write1(out, aaload); write1(out, ldc_w); write2(out, poolAddInteger(pool, parameterCount) + 1); write1(out, anewarray); write2(out, poolAddClass(pool, "java/lang/Object") + 1); int ai = 0; int si; for (si = 1; spec[si] != ')'; ++si) { write1(out, dup); write1(out, ldc_w); write2(out, poolAddInteger(pool, ai) + 1); switch (spec[si]) { case 'L': ++ si; while (spec[si] != ';') ++si; write1(out, aload); write1(out, ai + 1); break; case '[': ++ si; while (spec[si] == '[') ++si; switch (spec[si]) { case 'L': ++ si; while (spec[si] != ';') ++si; break; default: break; } write1(out, aload); write1(out, ai + 1); break; case 'Z': write1(out, iload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;") + 1); break; case 'B': write1(out, iload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;") + 1); break; case 'S': write1(out, iload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;") + 1); break; case 'C': write1(out, iload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;") + 1); break; case 'I': write1(out, iload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;") + 1); break; case 'F': write1(out, fload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;") + 1); break; case 'J': write1(out, lload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;") + 1); ++ ai; break; case 'D': write1(out, dload); write1(out, ai + 1); write1(out, invokestatic); write2(out, poolAddMethodRef (pool, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;") + 1); ++ ai; break; default: throw new IllegalArgumentException(); } write1(out, aastore); ++ ai; } write1(out, invokeinterface); write2(out, poolAddMethodRef (pool, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;" + "Ljava/lang/reflect/Method;" + "[Ljava/lang/Object;)" + "Ljava/lang/Object;") + 1); write2(out, 0); // this will be ignored by the VM switch (spec[si + 1]) { case 'L': case '[': write1(out, areturn); break; case 'Z': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Boolean", "booleanValue", "()Z") + 1); write1(out, ireturn); break; case 'B': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Byte", "byteValue", "()B") + 1); write1(out, ireturn); break; case 'C': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Character", "charValue", "()C") + 1); write1(out, ireturn); break; case 'S': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Short", "shortValue", "()S") + 1); write1(out, ireturn); break; case 'I': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Integer", "intValue", "()I") + 1); write1(out, ireturn); break; case 'F': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Float", "floatValue", "()F") + 1); write1(out, freturn); break; case 'J': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Long", "longValue", "()J") + 1); write1(out, lreturn); break; case 'D': write1(out, invokevirtual); write2(out, poolAddMethodRef (pool, "java/lang/Double", "doubleValue", "()D") + 1); write1(out, dreturn); break; case 'V': write1(out, pop); write1(out, return_); break; default: throw new IllegalArgumentException(); } write2(out, 0); // exception handler table length write2(out, 0); // attribute count byte[] result = out.toByteArray(); set4(result, 4, result.length - 12); return result; } private static byte[] makeConstructorCode(List pool) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); write2(out, 2); // max stack write2(out, 2); // max locals write4(out, 6); // length write1(out, aload_0); write1(out, aload_1); write1(out, putfield); write2(out, poolAddFieldRef (pool, "java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;") + 1); write1(out, return_); write2(out, 0); // exception handler table length write2(out, 0); // attribute count return out.toByteArray(); } private static Class makeClass(ClassLoader loader, Class[] interfaces, String name) throws IOException { List pool = new ArrayList(); int[] interfaceIndexes = new int[interfaces.length]; for (int i = 0; i < interfaces.length; ++i) { interfaceIndexes[i] = poolAddClass(pool, interfaces[i].getName()); } Map virtualMap = new HashMap(); for (Class c: interfaces) { Method[] ivtable = c.virtualTable; if (ivtable != null) { for (Method m: ivtable) { virtualMap.put(m.getName() + m.getSpec(), m); } } } MethodData[] methodTable = new MethodData[virtualMap.size() + 1]; { int i = 0; for (Method m: virtualMap.values()) { methodTable[i] = new MethodData (poolAddUtf8(pool, m.getName()), poolAddUtf8(pool, m.getSpec()), makeInvokeCode(pool, name, m.spec, m.parameterCount, m.parameterFootprint, i)); ++ i; } methodTable[i++] = new MethodData (poolAddUtf8(pool, ""), poolAddUtf8(pool, "(Ljava/lang/reflect/InvocationHandler;)V"), makeConstructorCode(pool)); } int nameIndex = poolAddClass(pool, name); int superIndex = poolAddClass(pool, "java/lang/reflect/Proxy"); int codeAttributeNameIndex = poolAddUtf8(pool, "Code"); ByteArrayOutputStream out = new ByteArrayOutputStream(); write4(out, 0xCAFEBABE); write2(out, 0); // minor version write2(out, 0); // major version write2(out, pool.size() + 1); for (PoolEntry e: pool) { e.writeTo(out); } write2(out, 0); // flags write2(out, nameIndex + 1); write2(out, superIndex + 1); write2(out, interfaces.length); for (int i: interfaceIndexes) { write2(out, i + 1); } write2(out, 0); // field count write2(out, methodTable.length); for (MethodData m: methodTable) { write2(out, 0); // flags write2(out, m.nameIndex + 1); write2(out, m.specIndex + 1); write2(out, 1); // attribute count write2(out, codeAttributeNameIndex + 1); write4(out, m.code.length); out.write(m.code); } write2(out, 0); // attribute count byte[] classData = out.toByteArray(); return avian.SystemClassLoader.defineClass (loader, classData, 0, classData.length); } public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler) { try { return Proxy.getProxyClass(loader, interfaces) .getConstructor(new Class[] { InvocationHandler.class }) .newInstance(new Object[] { handler }); } catch (Exception e) { AssertionError error = new AssertionError(); error.initCause(e); throw error; } } private static class MethodData { public final int nameIndex; public final int specIndex; public final byte[] code; public MethodData(int nameIndex, int specIndex, byte[] code) { this.nameIndex = nameIndex; this.specIndex = specIndex; this.code = code; } } public interface PoolEntry { public void writeTo(OutputStream out) throws IOException; } public static class IntegerPoolEntry implements PoolEntry { private final int value; public IntegerPoolEntry(int value) { this.value = value; } public void writeTo(OutputStream out) throws IOException { write1(out, CONSTANT_Integer); write4(out, value); } public boolean equals(Object o) { return o instanceof IntegerPoolEntry && ((IntegerPoolEntry) o).value == value; } } public static class Utf8PoolEntry implements PoolEntry { private final String data; public Utf8PoolEntry(String data) { this.data = data; } public void writeTo(OutputStream out) throws IOException { write1(out, CONSTANT_Utf8); byte[] bytes = data.getBytes(); write2(out, bytes.length); out.write(bytes); } public boolean equals(Object o) { return o instanceof Utf8PoolEntry && ((Utf8PoolEntry) o).data.equals(data); } } public static class ClassPoolEntry implements PoolEntry { private final int nameIndex; public ClassPoolEntry(int nameIndex) { this.nameIndex = nameIndex; } public void writeTo(OutputStream out) throws IOException { write1(out, CONSTANT_Class); write2(out, nameIndex + 1); } public boolean equals(Object o) { return o instanceof ClassPoolEntry && ((ClassPoolEntry) o).nameIndex == nameIndex; } } public static class NameAndTypePoolEntry implements PoolEntry { private final int nameIndex; private final int typeIndex; public NameAndTypePoolEntry(int nameIndex, int typeIndex) { this.nameIndex = nameIndex; this.typeIndex = typeIndex; } public void writeTo(OutputStream out) throws IOException { write1(out, CONSTANT_NameAndType); write2(out, nameIndex + 1); write2(out, typeIndex + 1); } public boolean equals(Object o) { if (o instanceof NameAndTypePoolEntry) { NameAndTypePoolEntry other = (NameAndTypePoolEntry) o; return other.nameIndex == nameIndex && other.typeIndex == typeIndex; } else { return false; } } } public static class FieldRefPoolEntry implements PoolEntry { private final int classIndex; private final int nameAndTypeIndex; public FieldRefPoolEntry(int classIndex, int nameAndTypeIndex) { this.classIndex = classIndex; this.nameAndTypeIndex = nameAndTypeIndex; } public void writeTo(OutputStream out) throws IOException { write1(out, CONSTANT_Fieldref); write2(out, classIndex + 1); write2(out, nameAndTypeIndex + 1); } public boolean equals(Object o) { if (o instanceof FieldRefPoolEntry) { FieldRefPoolEntry other = (FieldRefPoolEntry) o; return other.classIndex == classIndex && other.nameAndTypeIndex == nameAndTypeIndex; } else { return false; } } } public static class MethodRefPoolEntry implements PoolEntry { private final int classIndex; private final int nameAndTypeIndex; public MethodRefPoolEntry(int classIndex, int nameAndTypeIndex) { this.classIndex = classIndex; this.nameAndTypeIndex = nameAndTypeIndex; } public void writeTo(OutputStream out) throws IOException { write1(out, CONSTANT_Methodref); write2(out, classIndex + 1); write2(out, nameAndTypeIndex + 1); } public boolean equals(Object o) { if (o instanceof MethodRefPoolEntry) { MethodRefPoolEntry other = (MethodRefPoolEntry) o; return other.classIndex == classIndex && other.nameAndTypeIndex == nameAndTypeIndex; } else { return false; } } } }