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

@ -0,0 +1,116 @@
/* Copyright (c) 2011, 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 avian;
import static avian.Stream.write1;
import static avian.Stream.write2;
import static avian.Stream.write4;
import avian.ConstantPool.PoolEntry;
import java.util.List;
import java.io.OutputStream;
import java.io.IOException;
public class Assembler {
public static final int ACC_PUBLIC = 1 << 0;
public static final int ACC_STATIC = 1 << 3;
public static final int aaload = 0x32;
public static final int aastore = 0x53;
public static final int aload = 0x19;
public static final int aload_0 = 0x2a;
public static final int aload_1 = 0x2b;
public static final int astore_0 = 0x4b;
public static final int anewarray = 0xbd;
public static final int areturn = 0xb0;
public static final int dload = 0x18;
public static final int dreturn = 0xaf;
public static final int dup = 0x59;
public static final int fload = 0x17;
public static final int freturn = 0xae;
public static final int getfield = 0xb4;
public static final int goto_ = 0xa7;
public static final int iload = 0x15;
public static final int invokeinterface = 0xb9;
public static final int invokespecial = 0xb7;
public static final int invokestatic = 0xb8;
public static final int invokevirtual = 0xb6;
public static final int ireturn = 0xac;
public static final int jsr = 0xa8;
public static final int ldc_w = 0x13;
public static final int lload = 0x16;
public static final int lreturn = 0xad;
public static final int new_ = 0xbb;
public static final int pop = 0x57;
public static final int putfield = 0xb5;
public static final int ret = 0xa9;
public static final int return_ = 0xb1;
public static void writeClass(OutputStream out,
List<PoolEntry> pool,
int name,
int super_,
int[] interfaces,
MethodData[] methods)
throws IOException
{
int codeAttributeName = ConstantPool.addUtf8(pool, "Code");
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, name + 1);
write2(out, super_ + 1);
write2(out, interfaces.length);
for (int i: interfaces) {
write2(out, i + 1);
}
write2(out, 0); // field count
write2(out, methods.length);
for (MethodData m: methods) {
write2(out, m.flags);
write2(out, m.nameIndex + 1);
write2(out, m.specIndex + 1);
write2(out, 1); // attribute count
write2(out, codeAttributeName + 1);
write4(out, m.code.length);
out.write(m.code);
}
write2(out, 0); // attribute count
}
public static class MethodData {
public final int flags;
public final int nameIndex;
public final int specIndex;
public final byte[] code;
public MethodData(int flags, int nameIndex, int specIndex, byte[] code) {
this.flags = flags;
this.nameIndex = nameIndex;
this.specIndex = specIndex;
this.code = code;
}
}
}

View File

@ -0,0 +1,242 @@
/* Copyright (c) 2011, 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 avian;
import static avian.Stream.write1;
import static avian.Stream.write2;
import static avian.Stream.write4;
import java.util.List;
import java.io.OutputStream;
import java.io.IOException;
public class ConstantPool {
private static final int CONSTANT_Integer = 3;
private static final int CONSTANT_Utf8 = 1;
private static final int CONSTANT_String = 8;
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;
public static int add(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;
}
public static int addInteger(List<PoolEntry> pool, int value) {
return add(pool, new IntegerPoolEntry(value));
}
public static int addUtf8(List<PoolEntry> pool, String value) {
return add(pool, new Utf8PoolEntry(value));
}
public static int addString(List<PoolEntry> pool, String value) {
return add(pool, new StringPoolEntry(addUtf8(pool, value)));
}
public static int addClass(List<PoolEntry> pool, String name) {
return add(pool, new ClassPoolEntry(addUtf8(pool, name)));
}
public static int addNameAndType(List<PoolEntry> pool,
String name,
String type)
{
return add(pool, new NameAndTypePoolEntry
(addUtf8(pool, name),
addUtf8(pool, type)));
}
public static int addFieldRef(List<PoolEntry> pool,
String className,
String name,
String spec)
{
return add(pool, new FieldRefPoolEntry
(addClass(pool, className),
addNameAndType(pool, name, spec)));
}
public static int addMethodRef(List<PoolEntry> pool,
String className,
String name,
String spec)
{
return add(pool, new MethodRefPoolEntry
(addClass(pool, className),
addNameAndType(pool, name, spec)));
}
public interface PoolEntry {
public void writeTo(OutputStream out) throws IOException;
}
private 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;
}
}
private 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);
}
}
private static class StringPoolEntry implements PoolEntry {
private final int valueIndex;
public StringPoolEntry(int valueIndex) {
this.valueIndex = valueIndex;
}
public void writeTo(OutputStream out) throws IOException {
write1(out, CONSTANT_String);
write2(out, valueIndex + 1);
}
public boolean equals(Object o) {
return o instanceof StringPoolEntry
&& ((StringPoolEntry) o).valueIndex == valueIndex;
}
}
private 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;
}
}
private 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;
}
}
}
private 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;
}
}
}
private 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;
}
}
}
}

View File

@ -71,4 +71,10 @@ public abstract class Stream {
(b5 << 24) | (b6 << 16) | (b7 << 8) | (b8));
}
public 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);
}
}

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;
}
}
}
}

View File

@ -636,7 +636,7 @@ $(test-dep): $(test-sources)
$(javac) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \
fi
$(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \
test/Subroutine.java
-bootclasspath $(boot-classpath) test/Subroutine.java
@touch $(@)
$(test-extra-dep): $(test-extra-sources)

View File

@ -1,8 +1,93 @@
import avian.Stream;
import avian.ConstantPool;
import avian.ConstantPool.PoolEntry;
import avian.Assembler;
import avian.Assembler.MethodData;
import java.util.ArrayList;
import java.util.List;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.IOException;
public class Subroutine {
private static void expect(boolean v) {
if (! v) throw new RuntimeException();
}
private static byte[] makeTestCode(List pool) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Stream.write2(out, 1); // max stack
Stream.write2(out, 1); // max locals
Stream.write4(out, 0); // length (we'll set the real value later)
// 0:
Stream.write1(out, Assembler.ldc_w);
Stream.write2(out, ConstantPool.addString(pool, "foo") + 1);
// 3:
Stream.write1(out, Assembler.astore_0);
// 4:
Stream.write1(out, Assembler.invokestatic);
Stream.write2(out, ConstantPool.addMethodRef
(pool, "java/lang/System", "gc", "()V") + 1);
// 7:
Stream.write1(out, Assembler.goto_);
Stream.write2(out, 9); // 16
// 10:
Stream.write1(out, Assembler.astore_0);
// 11:
Stream.write1(out, Assembler.invokestatic);
Stream.write2(out, ConstantPool.addMethodRef
(pool, "java/lang/System", "gc", "()V") + 1);
// 14:
Stream.write1(out, Assembler.ret);
Stream.write1(out, 0);
// 16:
Stream.write1(out, Assembler.jsr);
Stream.write2(out, -6); // 10
// 19:
Stream.write1(out, Assembler.invokestatic);
Stream.write2(out, ConstantPool.addMethodRef
(pool, "java/lang/System", "gc", "()V") + 1);
// 22:
Stream.write1(out, Assembler.return_);
Stream.write2(out, 0); // exception handler table length
Stream.write2(out, 0); // attribute count
byte[] result = out.toByteArray();
Stream.set4(result, 4, result.length - 12);
return result;
}
private static Class makeTestClass() throws IOException {
List pool = new ArrayList();
ByteArrayOutputStream out = new ByteArrayOutputStream();
String name = "$SubroutineTest$";
Assembler.writeClass
(out, pool, ConstantPool.addClass(pool, name),
ConstantPool.addClass(pool, "java/lang/Object"),
new int[0], new MethodData[]
{ new MethodData(Assembler.ACC_STATIC | Assembler.ACC_PUBLIC,
ConstantPool.addUtf8(pool, "test"),
ConstantPool.addUtf8(pool, "()V"),
makeTestCode(pool)) });
return new MyClassLoader(Subroutine.class.getClassLoader())
.defineClass(name, out.toByteArray());
}
// These tests are intended to cover the jsr and ret instructions.
// However, recent Sun javac versions avoid generating these
// instructions by default, so we must compile this class using
@ -138,7 +223,7 @@ public class Subroutine {
}
}
public boolean test5(boolean predicate) {
private boolean test5(boolean predicate) {
try {
if (predicate) {
return false;
@ -151,60 +236,73 @@ public class Subroutine {
return true;
}
public static void main(String[] args) {
test(false, false);
test(false, true);
test(true, false);
public static void main(String[] args) throws Exception {
// test(false, false);
// test(false, true);
// test(true, false);
String.valueOf(test2(1));
String.valueOf(test2(2));
String.valueOf(test2(3));
// String.valueOf(test2(1));
// String.valueOf(test2(2));
// String.valueOf(test2(3));
String.valueOf(test3(1, 1, 1));
String.valueOf(test3(2, 1, 1));
String.valueOf(test3(3, 1, 1));
// String.valueOf(test3(1, 1, 1));
// String.valueOf(test3(2, 1, 1));
// String.valueOf(test3(3, 1, 1));
String.valueOf(test3(1, 2, 1));
String.valueOf(test3(2, 2, 1));
String.valueOf(test3(3, 2, 1));
// String.valueOf(test3(1, 2, 1));
// String.valueOf(test3(2, 2, 1));
// String.valueOf(test3(3, 2, 1));
String.valueOf(test3(1, 3, 1));
String.valueOf(test3(2, 3, 1));
String.valueOf(test3(3, 3, 1));
// String.valueOf(test3(1, 3, 1));
// String.valueOf(test3(2, 3, 1));
// String.valueOf(test3(3, 3, 1));
String.valueOf(test3(1, 1, 2));
String.valueOf(test3(2, 1, 2));
String.valueOf(test3(3, 1, 2));
// String.valueOf(test3(1, 1, 2));
// String.valueOf(test3(2, 1, 2));
// String.valueOf(test3(3, 1, 2));
String.valueOf(test3(1, 2, 2));
String.valueOf(test3(2, 2, 2));
String.valueOf(test3(3, 2, 2));
// String.valueOf(test3(1, 2, 2));
// String.valueOf(test3(2, 2, 2));
// String.valueOf(test3(3, 2, 2));
String.valueOf(test3(1, 3, 2));
String.valueOf(test3(2, 3, 2));
String.valueOf(test3(3, 3, 2));
// String.valueOf(test3(1, 3, 2));
// String.valueOf(test3(2, 3, 2));
// String.valueOf(test3(3, 3, 2));
String.valueOf(test3(1, 1, 3));
String.valueOf(test3(2, 1, 3));
String.valueOf(test3(3, 1, 3));
// String.valueOf(test3(1, 1, 3));
// String.valueOf(test3(2, 1, 3));
// String.valueOf(test3(3, 1, 3));
String.valueOf(test3(1, 2, 3));
String.valueOf(test3(2, 2, 3));
String.valueOf(test3(3, 2, 3));
// String.valueOf(test3(1, 2, 3));
// String.valueOf(test3(2, 2, 3));
// String.valueOf(test3(3, 2, 3));
String.valueOf(test3(1, 3, 3));
String.valueOf(test3(2, 3, 3));
String.valueOf(test3(3, 3, 3));
// String.valueOf(test3(1, 3, 3));
// String.valueOf(test3(2, 3, 3));
// String.valueOf(test3(3, 3, 3));
String.valueOf(test4(1));
String.valueOf(test4(2));
String.valueOf(test4(3));
// String.valueOf(test4(1));
// String.valueOf(test4(2));
// String.valueOf(test4(3));
expect(test4(1) == 0xFABFABFABFL);
// expect(test4(1) == 0xFABFABFABFL);
new Subroutine().test5(true);
new Subroutine().test5(false);
// new Subroutine().test5(true);
// new Subroutine().test5(false);
makeTestClass().getMethod("test", new Class[0]).invoke
(null, new Object[0]);
}
private static class DummyException extends RuntimeException { }
private static class MyClassLoader extends ClassLoader {
public MyClassLoader(ClassLoader parent) {
super(parent);
}
public Class defineClass(String name, byte[] bytes) {
return super.defineClass(name, bytes, 0, bytes.length);
}
}
}