2013-07-03 02:52:38 +00:00
|
|
|
/* Copyright (c) 2008-2013, Avian Contributors
|
2010-09-10 21:05:29 +00:00
|
|
|
|
|
|
|
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.read1;
|
|
|
|
import static avian.Stream.read2;
|
|
|
|
|
|
|
|
import java.lang.reflect.Modifier;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.lang.reflect.Field;
|
2013-02-21 22:37:17 +00:00
|
|
|
import java.lang.reflect.Proxy;
|
|
|
|
import java.lang.annotation.Annotation;
|
2010-09-10 21:05:29 +00:00
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
|
|
|
|
public class Classes {
|
|
|
|
private static final int LinkFlag = 1 << 8;
|
|
|
|
|
|
|
|
public static native VMClass defineVMClass
|
|
|
|
(ClassLoader loader, byte[] b, int offset, int length);
|
|
|
|
|
|
|
|
public static native VMClass primitiveClass(char name);
|
|
|
|
|
|
|
|
public static native void initialize(VMClass vmClass);
|
|
|
|
|
|
|
|
public static native boolean isAssignableFrom(VMClass a, VMClass b);
|
|
|
|
|
|
|
|
public static native VMClass getVMClass(Object o);
|
|
|
|
|
|
|
|
private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec)
|
|
|
|
throws ClassNotFoundException;
|
|
|
|
|
|
|
|
private static VMClass loadVMClass(ClassLoader loader,
|
|
|
|
byte[] nameBytes, int offset, int length)
|
|
|
|
{
|
|
|
|
byte[] spec = new byte[length + 1];
|
|
|
|
System.arraycopy(nameBytes, offset, spec, 0, length);
|
|
|
|
|
|
|
|
try {
|
|
|
|
VMClass c = resolveVMClass(loader, spec);
|
|
|
|
if (c == null) {
|
|
|
|
throw new NoClassDefFoundError();
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
NoClassDefFoundError error = new NoClassDefFoundError
|
2011-03-27 05:24:48 +00:00
|
|
|
(new String(nameBytes, offset, length));
|
2010-09-10 21:05:29 +00:00
|
|
|
error.initCause(e);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Object parseAnnotationValue(ClassLoader loader,
|
|
|
|
Object pool,
|
|
|
|
InputStream in)
|
|
|
|
throws IOException
|
|
|
|
{
|
|
|
|
switch (read1(in)) {
|
|
|
|
case 'Z':
|
|
|
|
return Boolean.valueOf(Singleton.getInt(pool, read2(in) - 1) != 0);
|
|
|
|
|
|
|
|
case 'B':
|
|
|
|
return Byte.valueOf((byte) Singleton.getInt(pool, read2(in) - 1));
|
|
|
|
|
|
|
|
case 'C':
|
|
|
|
return Character.valueOf((char) Singleton.getInt(pool, read2(in) - 1));
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
return Short.valueOf((short) Singleton.getInt(pool, read2(in) - 1));
|
|
|
|
|
|
|
|
case 'I':
|
|
|
|
return Integer.valueOf(Singleton.getInt(pool, read2(in) - 1));
|
|
|
|
|
|
|
|
case 'F':
|
|
|
|
return Float.valueOf
|
|
|
|
(Float.intBitsToFloat(Singleton.getInt(pool, read2(in) - 1)));
|
|
|
|
|
|
|
|
case 'J': {
|
|
|
|
return Long.valueOf(Singleton.getLong(pool, read2(in) - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'D': {
|
|
|
|
return Double.valueOf
|
|
|
|
(Double.longBitsToDouble(Singleton.getLong(pool, read2(in) - 1)));
|
|
|
|
}
|
|
|
|
|
|
|
|
case 's': {
|
|
|
|
byte[] data = (byte[]) Singleton.getObject(pool, read2(in) - 1);
|
|
|
|
|
2011-03-27 05:24:48 +00:00
|
|
|
return new String(data, 0, data.length - 1);
|
2010-09-10 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case 'e': {
|
|
|
|
byte[] typeName = (byte[]) Singleton.getObject(pool, read2(in) - 1);
|
|
|
|
byte[] name = (byte[]) Singleton.getObject(pool, read2(in) - 1);
|
|
|
|
|
|
|
|
return Enum.valueOf
|
2010-09-14 16:49:41 +00:00
|
|
|
(SystemClassLoader.getClass
|
|
|
|
(loadVMClass(loader, typeName, 1, typeName.length - 3)),
|
2011-03-27 05:24:48 +00:00
|
|
|
new String(name, 0, name.length - 1));
|
2010-09-10 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case 'c':{
|
|
|
|
byte[] name = (byte[]) Singleton.getObject(pool, read2(in) - 1);
|
|
|
|
|
2010-09-25 21:51:30 +00:00
|
|
|
return SystemClassLoader.getClass
|
|
|
|
(loadVMClass(loader, name, 1, name.length - 3));
|
2010-09-10 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case '@':
|
|
|
|
return parseAnnotation(loader, pool, in);
|
|
|
|
|
|
|
|
case '[': {
|
|
|
|
Object[] array = new Object[read2(in)];
|
|
|
|
for (int i = 0; i < array.length; ++i) {
|
|
|
|
array[i] = parseAnnotationValue(loader, pool, in);
|
|
|
|
}
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: throw new AssertionError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Object[] parseAnnotation(ClassLoader loader,
|
|
|
|
Object pool,
|
|
|
|
InputStream in)
|
|
|
|
throws IOException
|
|
|
|
{
|
|
|
|
byte[] typeName = (byte[]) Singleton.getObject(pool, read2(in) - 1);
|
|
|
|
Object[] annotation = new Object[(read2(in) + 1) * 2];
|
2010-09-14 16:49:41 +00:00
|
|
|
annotation[1] = SystemClassLoader.getClass
|
2010-09-10 21:05:29 +00:00
|
|
|
(loadVMClass(loader, typeName, 1, typeName.length - 3));
|
|
|
|
|
|
|
|
for (int i = 2; i < annotation.length; i += 2) {
|
|
|
|
byte[] name = (byte[]) Singleton.getObject(pool, read2(in) - 1);
|
2011-03-27 05:24:48 +00:00
|
|
|
annotation[i] = new String(name, 0, name.length - 1);
|
2010-09-10 21:05:29 +00:00
|
|
|
annotation[i + 1] = parseAnnotationValue(loader, pool, in);
|
|
|
|
}
|
|
|
|
|
|
|
|
return annotation;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Object[] parseAnnotationTable(ClassLoader loader,
|
|
|
|
Object pool,
|
|
|
|
InputStream in)
|
|
|
|
throws IOException
|
|
|
|
{
|
|
|
|
Object[] table = new Object[read2(in)];
|
|
|
|
for (int i = 0; i < table.length; ++i) {
|
|
|
|
table[i] = parseAnnotation(loader, pool, in);
|
|
|
|
}
|
|
|
|
return table;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void parseAnnotationTable(ClassLoader loader,
|
|
|
|
Addendum addendum)
|
|
|
|
{
|
|
|
|
if (addendum != null && addendum.annotationTable instanceof byte[]) {
|
|
|
|
try {
|
|
|
|
addendum.annotationTable = parseAnnotationTable
|
|
|
|
(loader, addendum.pool, new ByteArrayInputStream
|
|
|
|
((byte[]) addendum.annotationTable));
|
|
|
|
} catch (IOException e) {
|
|
|
|
AssertionError error = new AssertionError();
|
|
|
|
error.initCause(e);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int resolveSpec(ClassLoader loader, byte[] spec, int start) {
|
|
|
|
int result;
|
|
|
|
int end;
|
|
|
|
switch (spec[start]) {
|
|
|
|
case 'L':
|
|
|
|
++ start;
|
|
|
|
end = start;
|
|
|
|
while (spec[end] != ';') ++ end;
|
|
|
|
result = end + 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '[':
|
|
|
|
end = start + 1;
|
|
|
|
while (spec[end] == '[') ++ end;
|
|
|
|
switch (spec[end]) {
|
|
|
|
case 'L':
|
|
|
|
++ end;
|
|
|
|
while (spec[end] != ';') ++ end;
|
|
|
|
++ end;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
++ end;
|
|
|
|
}
|
|
|
|
result = end;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return start + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
loadVMClass(loader, spec, start, end - start);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void link(VMClass c, ClassLoader loader) {
|
|
|
|
acquireClassLock();
|
|
|
|
try {
|
|
|
|
if ((c.vmFlags & LinkFlag) == 0) {
|
|
|
|
if (c.super_ != null) {
|
|
|
|
link(c.super_, loader);
|
|
|
|
}
|
|
|
|
|
|
|
|
parseAnnotationTable(loader, c.addendum);
|
|
|
|
|
|
|
|
if (c.interfaceTable != null) {
|
|
|
|
int stride = ((c.flags & Modifier.INTERFACE) != 0 ? 1 : 2);
|
|
|
|
for (int i = 0; i < c.interfaceTable.length; i += stride) {
|
|
|
|
link((VMClass) c.interfaceTable[i], loader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c.methodTable != null) {
|
|
|
|
for (int i = 0; i < c.methodTable.length; ++i) {
|
|
|
|
VMMethod m = c.methodTable[i];
|
|
|
|
|
|
|
|
for (int j = 1; j < m.spec.length;) {
|
|
|
|
j = resolveSpec(loader, m.spec, j);
|
|
|
|
}
|
|
|
|
|
|
|
|
parseAnnotationTable(loader, m.addendum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c.fieldTable != null) {
|
|
|
|
for (int i = 0; i < c.fieldTable.length; ++i) {
|
|
|
|
VMField f = c.fieldTable[i];
|
|
|
|
|
|
|
|
resolveSpec(loader, f.spec, 0);
|
|
|
|
|
|
|
|
parseAnnotationTable(loader, f.addendum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.vmFlags |= LinkFlag;
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
releaseClassLock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void link(VMClass c) {
|
|
|
|
link(c, c.loader);
|
|
|
|
}
|
|
|
|
|
2013-02-21 22:37:17 +00:00
|
|
|
public static Class forName(String name, boolean initialize,
|
|
|
|
ClassLoader loader)
|
|
|
|
throws ClassNotFoundException
|
|
|
|
{
|
|
|
|
if (loader == null) {
|
|
|
|
loader = Class.class.getClassLoader();
|
|
|
|
}
|
|
|
|
Class c = loader.loadClass(name);
|
|
|
|
VMClass vmc = SystemClassLoader.vmClass(c);
|
|
|
|
Classes.link(vmc, loader);
|
|
|
|
if (initialize) {
|
|
|
|
Classes.initialize(vmc);
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class forCanonicalName(String name) {
|
|
|
|
return forCanonicalName(null, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class forCanonicalName(ClassLoader loader, String name) {
|
|
|
|
try {
|
|
|
|
if (name.startsWith("[")) {
|
|
|
|
return forName(name, true, loader);
|
|
|
|
} else if (name.startsWith("L")) {
|
|
|
|
return forName(name.substring(1, name.length() - 1), true, loader);
|
|
|
|
} else {
|
|
|
|
if (name.length() == 1) {
|
|
|
|
return SystemClassLoader.getClass
|
|
|
|
(Classes.primitiveClass(name.charAt(0)));
|
|
|
|
} else {
|
|
|
|
throw new ClassNotFoundException(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int next(char c, String s, int start) {
|
|
|
|
for (int i = start; i < s.length(); ++i) {
|
|
|
|
if (s.charAt(i) == c) return i;
|
|
|
|
}
|
|
|
|
throw new RuntimeException();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Class[] getParameterTypes(VMMethod vmMethod) {
|
|
|
|
int count = vmMethod.parameterCount;
|
|
|
|
|
|
|
|
Class[] types = new Class[count];
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
String spec = new String
|
2013-02-22 18:55:01 +00:00
|
|
|
(vmMethod.spec, 1, vmMethod.spec.length - 2);
|
2013-02-21 22:37:17 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
for (int i = 0; i < spec.length(); ++i) {
|
|
|
|
char c = spec.charAt(i);
|
|
|
|
if (c == ')') {
|
|
|
|
break;
|
|
|
|
} else if (c == 'L') {
|
|
|
|
int start = i + 1;
|
|
|
|
i = next(';', spec, start);
|
|
|
|
String name = spec.substring(start, i).replace('/', '.');
|
|
|
|
types[index++] = Class.forName(name, true, vmMethod.class_.loader);
|
|
|
|
} else if (c == '[') {
|
|
|
|
int start = i;
|
|
|
|
while (spec.charAt(i) == '[') ++i;
|
|
|
|
|
|
|
|
if (spec.charAt(i) == 'L') {
|
|
|
|
i = next(';', spec, i + 1);
|
|
|
|
String name = spec.substring(start, i).replace('/', '.');
|
|
|
|
types[index++] = Class.forName
|
|
|
|
(name, true, vmMethod.class_.loader);
|
|
|
|
} else {
|
|
|
|
String name = spec.substring(start, i + 1);
|
|
|
|
types[index++] = forCanonicalName(vmMethod.class_.loader, name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
String name = spec.substring(i, i + 1);
|
|
|
|
types[index++] = forCanonicalName(vmMethod.class_.loader, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
return types;
|
|
|
|
}
|
2013-02-22 18:55:01 +00:00
|
|
|
|
2013-02-21 22:37:17 +00:00
|
|
|
public static int findField(VMClass vmClass, String name) {
|
|
|
|
if (vmClass.fieldTable != null) {
|
|
|
|
Classes.link(vmClass);
|
|
|
|
|
|
|
|
for (int i = 0; i < vmClass.fieldTable.length; ++i) {
|
|
|
|
if (toString(vmClass.fieldTable[i].name).equals(name)) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String toString(byte[] array) {
|
|
|
|
return new String(array, 0, array.length - 1);
|
|
|
|
}
|
|
|
|
|
2013-02-22 18:55:01 +00:00
|
|
|
public static boolean match(Class[] a, Class[] b) {
|
2013-02-21 22:37:17 +00:00
|
|
|
if (a.length == b.length) {
|
|
|
|
for (int i = 0; i < a.length; ++i) {
|
|
|
|
if (! a[i].isAssignableFrom(b[i])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-22 18:55:01 +00:00
|
|
|
public static int findMethod(VMClass vmClass, String name,
|
2013-02-21 22:37:17 +00:00
|
|
|
Class[] parameterTypes)
|
|
|
|
{
|
|
|
|
if (vmClass.methodTable != null) {
|
|
|
|
Classes.link(vmClass);
|
|
|
|
|
|
|
|
if (parameterTypes == null) {
|
|
|
|
parameterTypes = new Class[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < vmClass.methodTable.length; ++i) {
|
|
|
|
if (toString(vmClass.methodTable[i].name).equals(name)
|
|
|
|
&& match(parameterTypes,
|
|
|
|
getParameterTypes(vmClass.methodTable[i])))
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-04-23 19:47:15 +00:00
|
|
|
public static int countMethods(VMClass vmClass, boolean publicOnly) {
|
|
|
|
int count = 0;
|
|
|
|
if (vmClass.methodTable != null) {
|
|
|
|
for (int i = 0; i < vmClass.methodTable.length; ++i) {
|
|
|
|
if (((! publicOnly)
|
|
|
|
|| ((vmClass.methodTable[i].flags & Modifier.PUBLIC))
|
|
|
|
!= 0)
|
|
|
|
&& (! toString(vmClass.methodTable[i].name).startsWith("<")))
|
|
|
|
{
|
|
|
|
++ count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Method[] getMethods(VMClass vmClass, boolean publicOnly) {
|
|
|
|
Method[] array = new Method[countMethods(vmClass, publicOnly)];
|
|
|
|
if (vmClass.methodTable != null) {
|
|
|
|
Classes.link(vmClass);
|
|
|
|
|
|
|
|
int ai = 0;
|
|
|
|
for (int i = 0; i < vmClass.methodTable.length; ++i) {
|
|
|
|
if ((((vmClass.methodTable[i].flags & Modifier.PUBLIC) != 0)
|
|
|
|
|| (! publicOnly))
|
|
|
|
&& ! toString(vmClass.methodTable[i].name).startsWith("<"))
|
|
|
|
{
|
|
|
|
array[ai++] = makeMethod(SystemClassLoader.getClass(vmClass), i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
2013-02-21 22:37:17 +00:00
|
|
|
public static Annotation getAnnotation(ClassLoader loader, Object[] a) {
|
|
|
|
if (a[0] == null) {
|
|
|
|
a[0] = Proxy.newProxyInstance
|
|
|
|
(loader, new Class[] { (Class) a[1] },
|
|
|
|
new AnnotationInvocationHandler(a));
|
|
|
|
}
|
|
|
|
return (Annotation) a[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
public static native Method makeMethod(Class c, int slot);
|
|
|
|
|
2010-09-10 21:05:29 +00:00
|
|
|
private static native void acquireClassLock();
|
|
|
|
|
|
|
|
private static native void releaseClassLock();
|
|
|
|
}
|