[e-lang] Joe-E reflection API questions
Mark Miller
erights at gmail.com
Wed Apr 18 16:49:15 CDT 2007
The current Joe-E + ref_send distribution (0.20) includes, as I
understand it, the non-controversial parts of Tyler's old Reflection
class, as the new Deflection class, in order to support Proxys and
InvocationHandlers, etc. This is almost adequate for Horton, but not
quite. I've went back to Tyler's old Reflection class, removed
everything that's been migrated to Deflection, removed Equality which
wasn't used by the remainder, and removed property/1. I include the
result below.
Why did I remove property/1? If the attached Reflection class were
made available to Joe-E programs, then the functionality of the old
property/1 could be written in Joe-E. I believe the remainder of
Reflection must be written in Java rather than Joe-E.
As far as I can tell, the attached Reflection class exports only safe
and sensible operations. I propose that it, or something of similar
functionality, be made available as part of the Joe-E library. Neither
Tyler nor I could adequately remember the nature of previous
controversies about this API. Since I do need some of this for Horton,
and Tyler needs some (all?) of this for reconstructing the waterken
web_send library on top of the new ref_send API, let's please argue
through these remaining controversies. Thanks.
For now, I will continue development of Horton while depending on
Joe-E, ref_send 0.20, and the Reflection class included below.
-----------------------------------------------------
// Copyright 2006 Regents of the University of California. May be used
// under the terms of the revised BSD license. See LICENSING for details.
package org.joe_e.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import org.joe_e.PowerlessArray;
/**
* The reflection interface.
* <p>
* This API provides reflective access to all the public constructors, public
* fields and public methods of public Joe-E classes and interfaces. The API
* provides no more permission than is provided by static Joe-E program code.
* If you can do something with the reflection API, you could also have done
* it using static Joe-E code. The only difference is expressivity.
* </p>
*/
public final class
Reflection {
private
Reflection() {}
/**
* The boot class loader.
*/
static private final ClassLoader boot = Runnable.class.getClassLoader();
/**
* indicates a request for static members
*/
static public final boolean statics = true;
/**
* indicates a request for instance members
*/
static public final boolean instance = false;
/**
* Gets a declared public field.
* @param statics either {@link #statics} or {@link #instance}
* @param type class to search
* @param name field name
* @return described field
* @throws NoSuchMember no matching field found
*/
static public Field
field(final boolean statics,
final Class type,
final String name) throws NoSuchMember {
// No reflection on non-public types.
if (!Modifier.isPublic(type.getModifiers())) {
throw new NoSuchMember(statics, type, name);
}
try {
final Field r = type.getDeclaredField(name);
final int m = r.getModifiers();
if (!Modifier.isPublic(m) || statics != Modifier.isStatic(m)) {
throw new NoSuchMember(statics, type, name);
}
if (!safe(r)) {
throw new NoSuchMember(statics, type, name);
}
return r;
} catch (final NoSuchFieldException _) {
throw new NoSuchMember(statics, type, name);
}
}
/**
* Gets all public fields.
* <p>
* If static fields are requested, this method only returns all public
* static fields declared by the specified class.
* </p>
* <p>
* If instance fields are requested, this method returns all public
* instance fields declared by the specified class, as well as any public
* instance fields declared by any extended class.
* </p>
* @param statics either {@link #statics} or {@link #instance}
* @param type object type
* @return described fields
*/
static public PowerlessArray<Field>
fields(final boolean statics, final Class type) {
Field[] r = type.getFields();
int n = 0;
if (statics) {
if (Modifier.isPublic(type.getModifiers())) {
for (final Field i : r) {
if (Modifier.isStatic(i.getModifiers()) &&
type == i.getDeclaringClass() && safe(i)) {
r[n++] = i;
}
}
}
} else {
for (final Field i : r) {
if (!Modifier.isStatic(i.getModifiers()) &&
Modifier.isPublic(i.getDeclaringClass().getModifiers()) &&
safe(i)) {
r[n++] = i;
}
}
}
if (r.length != n) {
System.arraycopy(r, 0, r = new Field[n], 0, n);
}
Arrays.sort(r, new Comparator<Field>() {
public int
compare(final Field a, final Field b) {
int d = a.getName().compareTo(b.getName());
if (0 == d) {
d = a.getDeclaringClass().getName().compareTo(
b.getDeclaringClass().getName());
}
return d;
}
});
return PowerlessArray.list(r);
}
/**
* Gets a declared public constructor.
* @param type class to search
* @param param each parameter type
* @return described constructor
* @throws NoSuchMember no matching constructor found
*/
static public Constructor
constructor(final Class type, final Class... param) throws NoSuchMember {
// No reflection on non-public types.
if (!Modifier.isPublic(type.getModifiers())) {
throw new NoSuchMember(true, type, "new");
}
try {
final Constructor r = type.getConstructor(param);
if (!safe(r)) {
throw new NoSuchMember(true, type, "new");
}
return r;
} catch (final NoSuchMethodException _) {
throw new NoSuchMember(true, type, "new");
}
}
/**
* Gets all declared public constructors.
* <p>
* This method only returns all public constructors declared by the
* specified class.
* </p>
* @param type class to search
* @return all public constructors
*/
static public PowerlessArray<Constructor>
constructors(final Class type) {
Constructor[] r = Modifier.isPublic(type.getModifiers())
? type.getConstructors()
: new Constructor[] {};
int n = 0;
for (final Constructor i : r) {
if (safe(i)) {
r[n++] = i;
}
}
if (r.length != n) {
System.arraycopy(r, 0, r = new Constructor[n], 0, n);
}
Arrays.sort(r, new Comparator<Constructor>() {
public int
compare(final Constructor a, final Constructor b) {
final Class[] pa = a.getParameterTypes();
final Class[] pb = b.getParameterTypes();
int d = pa.length != pb.length
? (pa.length < pb.length ? -1 : 1)
: 0;
for (int i = 0; 0 == d && i != pa.length; ++i) {
d = pa[i].getName().compareTo(pb[i].getName());
}
return d;
}
});
return PowerlessArray.list(r);
}
/**
* Gets a declared public method.
* @param statics either {@link #statics} or {@link #instance}
* @param type class to search
* @param name method name
* @param param each parameter type
* @return described method
* @throws NoSuchMember no matching method found
*/
static public Method
method(final boolean statics,
final Class type,
final String name,
final Class... param) throws NoSuchMember {
// No reflection on non-public types.
if (!Modifier.isPublic(type.getModifiers())) {
throw new NoSuchMember(statics, type, name);
}
try {
final Method r = type.getDeclaredMethod(name, param);
final int m = r.getModifiers();
if (!Modifier.isPublic(m) || statics != Modifier.isStatic(m)) {
throw new NoSuchMember(statics, type, name);
}
if (!safe(r)) {
throw new NoSuchMember(statics, type, name);
}
return r;
} catch (final NoSuchMethodException _) {
throw new NoSuchMember(statics, type, name);
}
}
/**
* Gets all public methods.
* <p>
* If static methods are requested, this method only returns all public
* static methods declared by the specified class.
* </p>
* <p>
* If instance methods are requested, this method returns all public
* instance methods declared by the specified class, as well as any public
* instance methods declared by any extended class, or implemented
* interface. Each returned method is a reflection of the method
* specification (ie: the super most declaration of the method), rather
* than a reflection of some override. This implementation ensures that
* a returned method can be {@link #invoke(Method, Object, Object[]) used}
* on any object that implements the specification type. For example,
* the following code will only lookup the "test" method once, but will
* successfully call both test method implementations:
* </p>
* <pre>
* interface Test {
* void test();
* }
* class A implements Test {
* void test() { … }
* }
* class B implements Test {
* void test() { … }
* }
* import static org.joe_e.reflection.Reflection.*;
* …
* private Method test = null;
* …
* public void
* test(final Object target) {
* if (null == test) {
* // Lookup and cache the call site.
* final PowerlessArray<Method> api =
* methods(instance, target.getClass());
* for (final Method m : api) {
* if ("test".equals(m.getName())) {
* test = m;
* break;
* }
* }
* }
* invoke(test, target);
* }
* …
* test(new A());
* test(new B());
* </pre>
* @param statics either {@link #statics} or {@link #instance}
* @param type object type
* @return described methods
*/
static public PowerlessArray<Method>
methods(final boolean statics, final Class type) {
Method[] r = type.getMethods();
int n = 0;
if (statics) {
if (Modifier.isPublic(type.getModifiers())) {
for (final Method i : r) {
if (Modifier.isStatic(i.getModifiers()) &&
type == i.getDeclaringClass() && safe(i)) {
r[n++] = i;
}
}
}
} else if (type.isInterface()) {
for (final Method i : r) {
if (Modifier.isPublic(i.getDeclaringClass().getModifiers()) &&
safe(i)) {
r[n++] = i;
}
}
} else {
for (final Method i : r) {
if (!Modifier.isStatic(i.getModifiers())) {
r[n] = bubble(i.getName(), i.getParameterTypes(),
i.getDeclaringClass(), i);
if (null != r[n]) {
++n;
}
}
}
}
if (r.length != n) {
System.arraycopy(r, 0, r = new Method[n], 0, n);
}
Arrays.sort(r, new Comparator<Method>() {
public int
compare(final Method a, final Method b) {
int d = a.getName().compareTo(b.getName());
if (0 == d) {
d = a.getDeclaringClass().getName().compareTo(
b.getDeclaringClass().getName());
if (0 == d) {
final Class[] pa = a.getParameterTypes();
final Class[] pb = b.getParameterTypes();
if (pa.length != pb.length) {
d = pa.length < pb.length ? -1 : 1;
}
for (int i = 0; 0 == d && i != pa.length; ++i) {
d = pa[i].getName().compareTo(pb[i].getName());
}
}
}
return d;
}
});
return PowerlessArray.list(r);
}
/**
* Find the super most safe declaration of the given method.
*/
static private Method
bubble(final String name, final Class[] param,
final Class declarer, Method method) {
if (!(Modifier.isPublic(declarer.getModifiers()) && safe(method))) {
method = null;
}
final Class parent = declarer.getSuperclass();
if (null != parent) {
try {
final Method specification =
bubble(name, param, parent, parent.getMethod(name,param));
if (null != specification) {
return specification;
}
} catch (final NoSuchMethodException _) {
}
}
for (final Class i : declarer.getInterfaces()) {
try {
final Method specification =
bubble(name, param, i, i.getMethod(name, param));
if (null != specification) {
return specification;
}
} catch (final NoSuchMethodException _) {
}
}
return method;
}
/**
* Does the given member obey Joe-E rules?
* @param member candidate member
* @return <code>true</code> if the member may be used by Joe-E code,
* else <code>false</code>
*/
static private boolean
safe(final Member member) {
final boolean r;
if (boot == member.getDeclaringClass().getClassLoader()) {
r = false; // TODO: Consult the taming database to enable.
} else {
// The declarer is user code, so assume it has passed the Joe-E
// verifier or has been audited for safety.
r = true;
}
return r;
}
}
More information about the e-lang
mailing list