mirror of
https://github.com/corda/corda.git
synced 2025-01-09 14:33:30 +00:00
664 lines
18 KiB
Java
664 lines
18 KiB
Java
/* 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<PoolEntry> 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<PoolEntry> pool, int value) {
|
|
return poolAdd(pool, new IntegerPoolEntry(value));
|
|
}
|
|
|
|
private static int poolAddUtf8(List<PoolEntry> pool, String value) {
|
|
return poolAdd(pool, new Utf8PoolEntry(value));
|
|
}
|
|
|
|
private static int poolAddClass(List<PoolEntry> pool, String name) {
|
|
return poolAdd(pool, new ClassPoolEntry(poolAddUtf8(pool, name)));
|
|
}
|
|
|
|
private static int poolAddNameAndType(List<PoolEntry> pool,
|
|
String name,
|
|
String type)
|
|
{
|
|
return poolAdd(pool, new NameAndTypePoolEntry
|
|
(poolAddUtf8(pool, name),
|
|
poolAddUtf8(pool, type)));
|
|
}
|
|
|
|
private static int poolAddFieldRef(List<PoolEntry> pool,
|
|
String className,
|
|
String name,
|
|
String spec)
|
|
{
|
|
return poolAdd(pool, new FieldRefPoolEntry
|
|
(poolAddClass(pool, className),
|
|
poolAddNameAndType(pool, name, spec)));
|
|
}
|
|
|
|
private static int poolAddMethodRef(List<PoolEntry> pool,
|
|
String className,
|
|
String name,
|
|
String spec)
|
|
{
|
|
return poolAdd(pool, new MethodRefPoolEntry
|
|
(poolAddClass(pool, className),
|
|
poolAddNameAndType(pool, name, spec)));
|
|
}
|
|
|
|
private static byte[] makeInvokeCode(List<PoolEntry> 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<PoolEntry> 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<PoolEntry> 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<String,Method> 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, "<init>"),
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|