001: package gnu.kawa.reflect;
002:
003: import gnu.bytecode.*;
004: import gnu.bytecode.ClassType;
005: import gnu.mapping.*;
006: import gnu.expr.*;
007: import gnu.lists.FString;
008: import java.util.Vector;
009:
010: public class ClassMethods extends Procedure2 {
011: public static final ClassMethods classMethods = new ClassMethods();
012: static {
013: classMethods.setName("class-methods");
014: }
015:
016: /** Create a method or generic of the matching methods.
017: * @param arg0 a Class, ClassType, or a String, FString or Symbol
018: * that names a class.
019: * @param arg1 a method name (a String, FString, or Symbol)
020: * Loosely the same as GetNamedPart.make(arg0, arg1),
021: * but with some extra conversions and checks.
022: */
023: public Object apply2(Object arg0, Object arg1) {
024: return apply(this , arg0, arg1);
025: }
026:
027: public static MethodProc apply(Procedure this Proc, Object arg0,
028: Object arg1) {
029: ClassType dtype;
030: String mname;
031: if (arg0 instanceof Class)
032: arg0 = Type.make((Class) arg0);
033: if (arg0 instanceof ClassType)
034: dtype = (ClassType) arg0;
035: else if (arg0 instanceof String || arg0 instanceof FString
036: || arg0 instanceof Symbol)
037: dtype = ClassType.make(arg0.toString());
038: else
039: throw new WrongType(this Proc, 0, null);
040: if (arg1 instanceof String || arg1 instanceof FString
041: || arg1 instanceof Symbol)
042: mname = arg1.toString();
043: else
044: throw new WrongType(this Proc, 1, null);
045: if (!("<init>".equals(mname)))
046: mname = Compilation.mangleName(mname);
047: MethodProc result = apply(dtype, mname, '\0', Language
048: .getDefaultLanguage());
049: if (result == null)
050: throw new RuntimeException("no applicable method named `"
051: + mname + "' in " + dtype.getName());
052: return result;
053: }
054:
055: private static int removeRedundantMethods(Vector methods) {
056: // Remove over-ridden methods.
057: int mlength = methods.size();
058: loopi: for (int i = 1; i < mlength;) {
059: Method method1 = (Method) methods.elementAt(i);
060: ClassType class1 = method1.getDeclaringClass();
061: Type[] types1 = method1.getParameterTypes();
062: int tlen = types1.length;
063: for (int j = 0; j < i; j++) {
064: Method method2 = (Method) methods.elementAt(j);
065: Type[] types2 = method2.getParameterTypes();
066: if (tlen != types2.length)
067: continue;
068: int k;
069: for (k = tlen; --k >= 0;) {
070: if (types1[k] != types2[k])
071: break;
072: }
073: if (k >= 0)
074: continue;
075: if (class1.isSubtype(method2.getDeclaringClass()))
076: methods.setElementAt(method1, j);
077: methods.setElementAt(methods.elementAt(mlength - 1), i);
078: mlength--;
079: // Re-do current i, since methods[i] replaced.
080: continue loopi;
081: }
082: i++;
083: }
084: return mlength;
085: }
086:
087: /** Return the methods of a class with the specified name and flag.
088: * @param caller if non-null, check that methods are accessible in it.
089: * @return an array containing the methods.
090: */
091: public static PrimProcedure[] getMethods(ObjectType dtype,
092: String mname, char mode, ClassType caller, Language language) {
093: MethodFilter filter = new MethodFilter(mname, 0, 0, caller);
094: // FIXME kludge until we handle "language types".
095: if (dtype == Type.tostring_type)
096: dtype = Type.string_type;
097: boolean named_class_only = mode == 'P'
098: || "<init>".equals(mname);
099: Vector methods = new Vector();
100: dtype.getMethods(filter, named_class_only ? 0 : 2, methods,
101: caller == null ? "-" : caller.getPackageName());
102:
103: int mlength = (named_class_only ? methods.size()
104: : removeRedundantMethods(methods));
105:
106: PrimProcedure[] result = new PrimProcedure[mlength];
107: int count = 0;
108: for (int i = mlength; --i >= 0;) {
109: Method method = (Method) methods.elementAt(i);
110: PrimProcedure pproc = new PrimProcedure(method, mode,
111: language);
112: result[count++] = pproc;
113: }
114: return result;
115: }
116:
117: /** Re-order the methods such that the ones that are definite
118: * applicable (all argtypes is subset of parameter type) are first;
119: * those possibly applicable next (argtype overlaps parameter types);
120: * and ending with those definitely not applicable (some argtype does
121: * overlap its parameter type).
122: * @return ((number of definitely applicable methods) << 32
123: * + (number of possibly applicable methods.
124: */
125: public static long selectApplicable(PrimProcedure[] methods,
126: Type[] atypes) {
127: int limit = methods.length;
128: int numDefApplicable = 0;
129: int numPosApplicable = 0;
130: for (int i = 0; i < limit;) {
131: int code = methods[i].isApplicable(atypes);
132: if (code < 0) { // Definitely not applicable.
133: // swap(methods[limit-1], methods[i]):
134: PrimProcedure tmp = methods[limit - 1];
135: methods[limit - 1] = methods[i];
136: methods[i] = tmp;
137: limit--;
138: } else if (code > 0) { // Definitely applicable.
139: // swap(methods[numDefApplicable], methods[i]):
140: PrimProcedure tmp = methods[numDefApplicable];
141: methods[numDefApplicable] = methods[i];
142: methods[i] = tmp;
143: numDefApplicable++;
144: i++;
145: } else { // Possibly applicable.
146: numPosApplicable++;
147: i++;
148: }
149: }
150: return (((long) numDefApplicable) << 32)
151: + (long) numPosApplicable;
152: }
153:
154: /** Find methods.
155: * @param dtype class to search
156: * @param mname method name (already mangled, if need be).
157: * @param mode one of 'P' (use invokespecial). 'V' (require this argument
158: * even if method is static), or '\0' (otherwise).
159: */
160: public static MethodProc apply(ObjectType dtype, String mname,
161: char mode, Language language) {
162: PrimProcedure[] methods = getMethods(dtype, mname, mode, null,
163: language);
164: GenericProc gproc = null;
165: PrimProcedure pproc = null;
166: for (int i = 0; i < methods.length; i++) {
167: PrimProcedure cur = methods[i];
168: if (pproc != null && gproc == null) {
169: gproc = new GenericProc();
170: gproc.add(pproc);
171: }
172: pproc = cur;
173: if (gproc != null)
174: gproc.add(pproc);
175: }
176: if (gproc != null) {
177: gproc.setName(dtype.getName() + "." + mname);
178: return gproc;
179: }
180: return pproc;
181: }
182:
183: /** Convert an expression to a name.
184: * @return a String if the expression has the form of a symbol or
185: * string literal, mangled as needed; otherwise null
186: */
187: static String checkName(Expression exp, boolean reversible) {
188: if (exp instanceof QuoteExp) {
189: Object name = ((QuoteExp) exp).getValue();
190: String nam;
191: if (name instanceof FString || name instanceof String)
192: nam = name.toString();
193: else if (name instanceof Symbol)
194: nam = ((Symbol) name).getName();
195: else
196: return null;
197: if (Compilation.isValidJavaName(nam))
198: return nam;
199: return Compilation.mangleName(nam, reversible);
200: }
201: return null;
202: }
203:
204: /** Convert an expression to a name.
205: * @return a String if the expression has the form of a symbol or
206: * string literal, with no mangling; otherwise null
207: */
208: static String checkName(Expression exp) {
209: if (exp instanceof QuoteExp) {
210: Object name = ((QuoteExp) exp).getValue();
211: if (name instanceof FString || name instanceof String)
212: return name.toString();
213: else if (name instanceof Symbol)
214: return ((Symbol) name).getName();
215: else
216: return null;
217: }
218: return null;
219: }
220: }
221:
222: class MethodFilter implements gnu.bytecode.Filter {
223: String name;
224: int nlen;
225: int modifiers;
226: int modmask;
227: ClassType caller;
228:
229: public MethodFilter(String name, int modifiers, int modmask,
230: ClassType caller) {
231: this .name = name;
232: this .nlen = name.length();
233: this .modifiers = modifiers;
234: this .modmask = modmask;
235: this .caller = caller;
236: }
237:
238: public boolean select(Object value) {
239: gnu.bytecode.Method method = (gnu.bytecode.Method) value;
240: String mname = method.getName();
241: int mmods = method.getModifiers();
242: if ((mmods & modmask) != modifiers || !mname.startsWith(name))
243: return false;
244: int mlen = mname.length();
245: char c;
246: if (mlen != nlen
247: && (mlen != nlen + 2 || mname.charAt(nlen) != '$' || ((c = mname
248: .charAt(nlen + 1)) != 'V' && c != 'X'))
249: && (mlen != nlen + 4 || !mname.endsWith("$V$X")))
250: return false;
251: return caller == null
252: || caller.isAccessible(method.getDeclaringClass(),
253: mmods);
254: }
255: }
|