PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/kawa-1.11/gnu/kawa/reflect/ClassMethods.java

#
Java | 338 lines | 278 code | 14 blank | 46 comment | 83 complexity | 0f936445e1905659ad9e362de427d584 MD5 | raw file
  1. package gnu.kawa.reflect;
  2. import gnu.bytecode.*;
  3. import gnu.bytecode.ClassType;
  4. import gnu.mapping.*;
  5. import gnu.expr.*;
  6. import gnu.lists.FString;
  7. import java.util.Vector;
  8. public class ClassMethods extends Procedure2
  9. {
  10. public static final ClassMethods classMethods = new ClassMethods();
  11. static { classMethods.setName("class-methods"); }
  12. /** Create a method or generic of the matching methods.
  13. * @param arg0 a Class, ClassType, or a String, FString or Symbol
  14. * that names a class.
  15. * @param arg1 a method name (a String, FString, or Symbol)
  16. * Loosely the same as GetNamedPart.make(arg0, arg1),
  17. * but with some extra conversions and checks.
  18. */
  19. public Object apply2 (Object arg0, Object arg1)
  20. {
  21. return apply(this, arg0, arg1);
  22. }
  23. public static MethodProc apply(Procedure thisProc, Object arg0, Object arg1)
  24. {
  25. ClassType dtype;
  26. String mname;
  27. if (arg0 instanceof Class)
  28. arg0 = Type.make((Class) arg0);
  29. if (arg0 instanceof ClassType)
  30. dtype = (ClassType) arg0;
  31. else if (arg0 instanceof String || arg0 instanceof FString
  32. || arg0 instanceof Symbol)
  33. dtype = ClassType.make(arg0.toString());
  34. else
  35. throw new WrongType(thisProc, 0, null);
  36. if (arg1 instanceof String || arg1 instanceof FString
  37. || arg1 instanceof Symbol)
  38. mname = arg1.toString();
  39. else
  40. throw new WrongType(thisProc, 1, null);
  41. if (! ("<init>".equals(mname)))
  42. mname = Compilation.mangleName(mname);
  43. MethodProc result = apply(dtype, mname, '\0', Language.getDefaultLanguage());
  44. if (result == null)
  45. throw new RuntimeException("no applicable method named `"+mname+"' in "
  46. +dtype.getName());
  47. return result;
  48. }
  49. private static int removeRedundantMethods(Vector methods)
  50. {
  51. // Remove over-ridden methods.
  52. int mlength = methods.size();
  53. loopi:
  54. for (int i = 1; i < mlength; )
  55. {
  56. Method method1 = (Method) methods.elementAt(i);
  57. ClassType class1 = method1.getDeclaringClass();
  58. Type[] types1 = method1.getParameterTypes();
  59. int tlen = types1.length;
  60. for (int j = 0; j < i; j++)
  61. {
  62. Method method2 = (Method) methods.elementAt(j);
  63. Type[] types2 = method2.getParameterTypes();
  64. if (tlen != types2.length)
  65. continue;
  66. int k;
  67. for (k = tlen; --k >= 0; )
  68. {
  69. if (types1[k] != types2[k])
  70. break;
  71. }
  72. if (k >= 0)
  73. continue;
  74. if (class1.isSubtype(method2.getDeclaringClass()))
  75. methods.setElementAt(method1, j);
  76. methods.setElementAt(methods.elementAt(mlength - 1), i);
  77. mlength--;
  78. // Re-do current i, since methods[i] replaced.
  79. continue loopi;
  80. }
  81. i++;
  82. }
  83. return mlength;
  84. }
  85. /** Return the methods of a class with the specified name and flag.
  86. * @param caller if non-null, check that methods are accessible in it.
  87. * @return an array containing the methods.
  88. */
  89. public static PrimProcedure[] getMethods(ObjectType dtype, String mname,
  90. char mode,
  91. ClassType caller,
  92. Language language)
  93. {
  94. // FIXME kludge until we handle "language types".
  95. if (dtype == Type.tostring_type)
  96. dtype = Type.string_type;
  97. MethodFilter filter = new MethodFilter(mname, 0, 0, caller,
  98. mode == 'P' ? null : dtype);
  99. boolean named_class_only = mode == 'P' || "<init>".equals(mname);
  100. Vector methods = new Vector();
  101. dtype.getMethods(filter, named_class_only ? 0 : 2, methods);
  102. if (! named_class_only &&
  103. // If not redundant (i.e. not a normal ClassType), also search Object.
  104. ! (dtype instanceof ClassType && ! ((ClassType) dtype).isInterface()))
  105. Type.pointer_type.getMethods(filter, 0, methods);
  106. int mlength = (named_class_only ? methods.size()
  107. : removeRedundantMethods(methods));
  108. PrimProcedure[] result = new PrimProcedure[mlength];
  109. int count = 0;
  110. for (int i = mlength; --i >= 0; )
  111. {
  112. Method method = (Method) methods.elementAt(i);
  113. if (! named_class_only && method.getDeclaringClass() != dtype)
  114. {
  115. Type itype = dtype.getImplementationType();
  116. if (itype instanceof ClassType)
  117. {
  118. // Override declared type of method so it matches receiver,
  119. // like javac does, for improved binary compatibility.
  120. method = new Method(method, (ClassType) itype);
  121. }
  122. }
  123. PrimProcedure pproc = new PrimProcedure(method, mode, language);
  124. result[count++] = pproc;
  125. }
  126. return result;
  127. }
  128. /** Re-order the methods such that the ones that are definite
  129. * applicable (all argtypes is subset of parameter type) are first;
  130. * those possibly applicable next (argtype overlaps parameter types);
  131. * and ending with those definitely not applicable (some argtype does
  132. * overlap its parameter type).
  133. * @return ((number of definitely applicable methods) << 32
  134. * + (number of possibly applicable methods.
  135. */
  136. public static long selectApplicable(PrimProcedure[] methods,
  137. Type[] atypes)
  138. {
  139. int limit = methods.length;
  140. int numDefApplicable = 0;
  141. int numPosApplicable = 0;
  142. for (int i = 0; i < limit; )
  143. {
  144. int code = methods[i].isApplicable(atypes);
  145. if (code < 0)
  146. { // Definitely not applicable.
  147. // swap(methods[limit-1], methods[i]):
  148. PrimProcedure tmp = methods[limit-1];
  149. methods[limit-1] = methods[i];
  150. methods[i] = tmp;
  151. limit--;
  152. }
  153. else if (code > 0)
  154. { // Definitely applicable.
  155. // swap(methods[numDefApplicable], methods[i]):
  156. PrimProcedure tmp = methods[numDefApplicable];
  157. methods[numDefApplicable] = methods[i];
  158. methods[i] = tmp;
  159. numDefApplicable++;
  160. i++;
  161. }
  162. else
  163. { // Possibly applicable.
  164. numPosApplicable++;
  165. i++;
  166. }
  167. }
  168. return (((long) numDefApplicable) << 32) + (long) numPosApplicable;
  169. }
  170. /** Select methods that have the right number of parameters.
  171. * @return number of methods that apply, NO_MATCH_TOO_FEW_ARGS,
  172. * or NO_MATCH_TOO_MANY_ARGS.
  173. */
  174. public static int selectApplicable(PrimProcedure[] methods, int numArgs)
  175. {
  176. int limit = methods.length;
  177. int numTooManyArgs = 0;
  178. int numTooFewArgs = 0;
  179. int numOk = 0;
  180. for (int i = 0; i < limit; )
  181. {
  182. int num = methods[i].numArgs();
  183. int min = Procedure.minArgs(num);
  184. int max = Procedure.maxArgs(num);
  185. boolean ok = false;
  186. if (numArgs < min)
  187. numTooFewArgs++;
  188. else if (numArgs > max && max >= 0)
  189. numTooManyArgs++;
  190. else
  191. ok = true;
  192. if (ok)
  193. {
  194. numOk++;
  195. i++;
  196. }
  197. else
  198. { // Not applicable.
  199. // swap(methods[limit-1], methods[i]):
  200. PrimProcedure tmp = methods[limit-1];
  201. methods[limit-1] = methods[i];
  202. methods[i] = tmp;
  203. limit--;
  204. }
  205. }
  206. return numOk > 0 ? numOk
  207. : numTooFewArgs > 0 ? MethodProc.NO_MATCH_TOO_FEW_ARGS
  208. : numTooManyArgs > 0 ? MethodProc.NO_MATCH_TOO_MANY_ARGS
  209. : 0;
  210. }
  211. /** Find methods.
  212. * @param dtype class to search
  213. * @param mname method name (already mangled, if need be).
  214. * @param mode one of 'P' (use invokespecial). 'V' (require this argument
  215. * even if method is static), or '\0' (otherwise).
  216. */
  217. public static MethodProc apply(ObjectType dtype, String mname,
  218. char mode, Language language)
  219. {
  220. PrimProcedure[] methods = getMethods(dtype, mname, mode, null, language);
  221. GenericProc gproc = null;
  222. PrimProcedure pproc = null;
  223. for (int i = 0; i < methods.length; i++)
  224. {
  225. PrimProcedure cur = methods[i];
  226. if (pproc != null && gproc == null)
  227. {
  228. gproc = new GenericProc();
  229. gproc.add(pproc);
  230. }
  231. pproc = cur;
  232. if (gproc != null)
  233. gproc.add(pproc);
  234. }
  235. if (gproc != null)
  236. {
  237. gproc.setName(dtype.getName()+"."+mname);
  238. return gproc;
  239. }
  240. return pproc;
  241. }
  242. /** Convert an expression to a name.
  243. * @return a String if the expression has the form of a symbol or
  244. * string literal, mangled as needed; otherwise null
  245. */
  246. static String checkName(Expression exp, boolean reversible)
  247. {
  248. if (exp instanceof QuoteExp)
  249. {
  250. Object name = ((QuoteExp) exp).getValue();
  251. String nam;
  252. if (name instanceof FString || name instanceof String)
  253. nam = name.toString();
  254. else if (name instanceof Symbol)
  255. nam = ((Symbol) name).getName();
  256. else
  257. return null;
  258. if (Compilation.isValidJavaName(nam))
  259. return nam;
  260. return Compilation.mangleName(nam, reversible);
  261. }
  262. return null;
  263. }
  264. /** Convert an expression to a name.
  265. * @return a String if the expression has the form of a symbol or
  266. * string literal, with no mangling; otherwise null
  267. */
  268. static String checkName(Expression exp)
  269. {
  270. if (exp instanceof QuoteExp)
  271. {
  272. Object name = ((QuoteExp) exp).getValue();
  273. if (name instanceof FString || name instanceof String)
  274. return name.toString();
  275. else if (name instanceof Symbol)
  276. return ((Symbol) name).getName();
  277. else
  278. return null;
  279. }
  280. return null;
  281. }
  282. }
  283. class MethodFilter implements gnu.bytecode.Filter
  284. {
  285. String name;
  286. int nlen;
  287. int modifiers;
  288. int modmask;
  289. ClassType caller;
  290. ObjectType receiver;
  291. public MethodFilter(String name, int modifiers, int modmask,
  292. ClassType caller, ObjectType receiver)
  293. {
  294. this.name = name;
  295. this.nlen = name.length();
  296. this.modifiers = modifiers;
  297. this.modmask = modmask;
  298. this.caller = caller;
  299. this.receiver = receiver;
  300. }
  301. public boolean select(Object value)
  302. {
  303. gnu.bytecode.Method method = (gnu.bytecode.Method) value;
  304. String mname = method.getName();
  305. int mmods = method.getModifiers();
  306. if ((mmods & modmask) != modifiers
  307. || (mmods & Access.SYNTHETIC) != 0
  308. || ! mname.startsWith(name))
  309. return false;
  310. int mlen = mname.length();
  311. char c;
  312. if (mlen != nlen
  313. && (mlen != nlen + 2
  314. || mname.charAt(nlen) != '$'
  315. || ((c = mname.charAt(nlen+1)) != 'V' && c != 'X'))
  316. && (mlen != nlen + 4
  317. || ! mname.endsWith("$V$X")))
  318. return false;
  319. ClassType declaring = receiver instanceof ClassType ? (ClassType) receiver
  320. : method.getDeclaringClass();
  321. return caller == null
  322. || caller.isAccessible(declaring, receiver, method.getModifiers());
  323. }
  324. }