add new subroutine test

This test covers the case where a local stack slot is first used to
store an object reference and later to store a subroutine return
address.  Unfortunately, this confuses the VM's stack mapping code;
I'll be working on a fix for that next.

The new test requires generating bytecode from scratch, since there's
no reliable way to get javac to generate the code we want.  Since we
already had primitive bytecode construction code in Proxy.java, I
factored it out so we can reuse it in Subroutine.java.
This commit is contained in:
Joel Dice
2011-02-16 11:41:33 -07:00
parent 0bbd11e9be
commit c743140e08
6 changed files with 553 additions and 355 deletions

View File

@ -13,6 +13,14 @@ package java.lang.reflect;
import static avian.Stream.write1;
import static avian.Stream.write2;
import static avian.Stream.write4;
import static avian.Stream.set4;
import static avian.Assembler.*;
import avian.ConstantPool;
import avian.ConstantPool.PoolEntry;
import avian.Assembler;
import avian.Assembler.MethodData;
import java.util.List;
import java.util.ArrayList;
@ -23,40 +31,6 @@ 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 invokespecial = 0xb7;
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 new_ = 0xbb;
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;
@ -92,67 +66,6 @@ public class Proxy {
return ((Proxy) proxy).h;
}
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,
@ -168,37 +81,37 @@ public class Proxy {
write1(out, aload_0);
write1(out, getfield);
write2(out, poolAddFieldRef
write2(out, ConstantPool.addFieldRef
(pool, "java/lang/reflect/Proxy",
"h", "Ljava/lang/reflect/InvocationHandler;") + 1);
write1(out, aload_0);
write1(out, new_);
write2(out, poolAddClass(pool, "java/lang/reflect/Method") + 1);
write2(out, ConstantPool.addClass(pool, "java/lang/reflect/Method") + 1);
write1(out, dup);
write1(out, ldc_w);
write2(out, poolAddClass(pool, className) + 1);
write2(out, ConstantPool.addClass(pool, className) + 1);
write1(out, getfield);
write2(out, poolAddFieldRef
write2(out, ConstantPool.addFieldRef
(pool, "java/lang/Class",
"vmClass", "Lavian/VMClass;") + 1);
write1(out, getfield);
write2(out, poolAddFieldRef
write2(out, ConstantPool.addFieldRef
(pool, "avian/VMClass",
"methodTable", "[Lavian/VMMethod;") + 1);
write1(out, ldc_w);
write2(out, poolAddInteger(pool, index) + 1);
write2(out, ConstantPool.addInteger(pool, index) + 1);
write1(out, aaload);
write1(out, invokespecial);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/reflect/Method",
"<init>", "(Lavian/VMMethod;)V") + 1);
write1(out, ldc_w);
write2(out, poolAddInteger(pool, parameterCount) + 1);
write2(out, ConstantPool.addInteger(pool, parameterCount) + 1);
write1(out, anewarray);
write2(out, poolAddClass(pool, "java/lang/Object") + 1);
write2(out, ConstantPool.addClass(pool, "java/lang/Object") + 1);
int ai = 0;
int si;
@ -206,7 +119,7 @@ public class Proxy {
write1(out, dup);
write1(out, ldc_w);
write2(out, poolAddInteger(pool, ai) + 1);
write2(out, ConstantPool.addInteger(pool, ai) + 1);
switch (spec[si]) {
case 'L':
@ -239,7 +152,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Boolean",
"valueOf", "(Z)Ljava/lang/Boolean;") + 1);
break;
@ -249,7 +162,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Byte",
"valueOf", "(B)Ljava/lang/Byte;") + 1);
break;
@ -259,7 +172,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Short",
"valueOf", "(S)Ljava/lang/Short;") + 1);
break;
@ -269,7 +182,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Character",
"valueOf", "(C)Ljava/lang/Character;") + 1);
break;
@ -279,7 +192,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Integer",
"valueOf", "(I)Ljava/lang/Integer;") + 1);
break;
@ -289,7 +202,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Float",
"valueOf", "(F)Ljava/lang/Float;") + 1);
break;
@ -299,7 +212,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Long",
"valueOf", "(J)Ljava/lang/Long;") + 1);
++ ai;
@ -310,7 +223,7 @@ public class Proxy {
write1(out, ai + 1);
write1(out, invokestatic);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Double",
"valueOf", "(D)Ljava/lang/Double;") + 1);
++ ai;
@ -325,7 +238,7 @@ public class Proxy {
}
write1(out, invokeinterface);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/reflect/InvocationHandler",
"invoke",
"(Ljava/lang/Object;"
@ -342,56 +255,56 @@ public class Proxy {
case 'Z':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Boolean", "booleanValue", "()Z") + 1);
write1(out, ireturn);
break;
case 'B':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Byte", "byteValue", "()B") + 1);
write1(out, ireturn);
break;
case 'C':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Character", "charValue", "()C") + 1);
write1(out, ireturn);
break;
case 'S':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Short", "shortValue", "()S") + 1);
write1(out, ireturn);
break;
case 'I':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Integer", "intValue", "()I") + 1);
write1(out, ireturn);
break;
case 'F':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Float", "floatValue", "()F") + 1);
write1(out, freturn);
break;
case 'J':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Long", "longValue", "()J") + 1);
write1(out, lreturn);
break;
case 'D':
write1(out, invokevirtual);
write2(out, poolAddMethodRef
write2(out, ConstantPool.addMethodRef
(pool, "java/lang/Double", "doubleValue", "()D") + 1);
write1(out, dreturn);
break;
@ -424,7 +337,7 @@ public class Proxy {
write1(out, aload_0);
write1(out, aload_1);
write1(out, putfield);
write2(out, poolAddFieldRef
write2(out, ConstantPool.addFieldRef
(pool, "java/lang/reflect/Proxy",
"h", "Ljava/lang/reflect/InvocationHandler;") + 1);
write1(out, return_);
@ -444,7 +357,8 @@ public class Proxy {
int[] interfaceIndexes = new int[interfaces.length];
for (int i = 0; i < interfaces.length; ++i) {
interfaceIndexes[i] = poolAddClass(pool, interfaces[i].getName());
interfaceIndexes[i] = ConstantPool.addClass
(pool, interfaces[i].getName());
}
Map<String,avian.VMMethod> virtualMap = new HashMap();
@ -461,57 +375,28 @@ public class Proxy {
{ int i = 0;
for (avian.VMMethod m: virtualMap.values()) {
methodTable[i] = new MethodData
(poolAddUtf8(pool, Method.getName(m)),
poolAddUtf8(pool, Method.getSpec(m)),
(0,
ConstantPool.addUtf8(pool, Method.getName(m)),
ConstantPool.addUtf8(pool, Method.getSpec(m)),
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"),
(0,
ConstantPool.addUtf8(pool, "<init>"),
ConstantPool.addUtf8
(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");
int nameIndex = ConstantPool.addClass(pool, name);
int superIndex = ConstantPool.addClass(pool, "java/lang/reflect/Proxy");
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
Assembler.writeClass
(out, pool, nameIndex, superIndex, interfaceIndexes, methodTable);
byte[] classData = out.toByteArray();
return avian.SystemClassLoader.getClass
@ -532,153 +417,4 @@ public class Proxy {
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;
}
}
}
}