MethodProc.java 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (c) 1999, 2004 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.mapping;
  4. import gnu.bytecode.Type;
  5. import gnu.bytecode.ArrayType;
  6. import gnu.expr.PrimProcedure;
  7. /* #ifdef use:java.lang.invoke */
  8. import java.lang.invoke.*;
  9. /* #else */
  10. // import gnu.mapping.CallContext.MethodHandle;
  11. /* #endif */
  12. /** Similar to a CLOS method.
  13. * Can check if arguments "match" before committing to calling method. */
  14. public abstract class MethodProc extends ProcedureN
  15. {
  16. public MethodProc() { // FIXME - remove
  17. super(true, applyToConsumerDefaultMP);
  18. }
  19. public MethodProc(boolean resultGoesToConsumer, MethodHandle applyMethod) {
  20. super(resultGoesToConsumer, applyMethod);
  21. }
  22. /** The parameter types.
  23. * Usually either an Type[] or a String encoding. */
  24. protected Object argTypes;
  25. /** Test if method is applicable to an invocation with given arguments.
  26. * @param argTypes array of known "single" arguments.
  27. * @param restType If null, the arguments are fully specified by argTypes.
  28. * If non-null, there may be an unknown number of extra arguments
  29. * of the given restType. This is used for splices, where we usually
  30. * don't know at compile-time how many argument values we have.
  31. * @return -1 if no; 1 if yes; 0 if need to check at run-time.
  32. */
  33. public int isApplicable(Type[] argTypes, Type restType) {
  34. int argCount = argTypes.length;
  35. int num = numArgs();
  36. int min = Procedure.minArgs(num);
  37. int max = Procedure.maxArgs(num);
  38. if ((argCount < min && restType == null)
  39. || (num >= 0 && argCount > max))
  40. return -1;
  41. int result = 1;
  42. for (int i = 0; ; i++ ) {
  43. if (i >= argCount && (restType == null || i >= min))
  44. break;
  45. Type argType = i < argCount ? argTypes[i] : restType;
  46. int code = getParameterType(i).isCompatibleWithValue(argType);
  47. if (code < 0) {
  48. return -1;
  49. }
  50. else if (code == 0)
  51. result = 0;
  52. }
  53. return result;
  54. }
  55. /** Return number of parameters, including optional and rest arguments. */
  56. public int numParameters()
  57. {
  58. int num = numArgs();
  59. int max = num >> 12;
  60. if (max >= 0)
  61. return max;
  62. // This isn't really right, but it works for PrimProcedure. FIXME.
  63. int min = num & 0xFFF;
  64. return min + 1;
  65. }
  66. static final Type[] unknownArgTypes = { Type.pointer_type };
  67. /** Figure out or decode the parameter types, setting argTypes. */
  68. protected void resolveParameterTypes()
  69. {
  70. argTypes = unknownArgTypes;
  71. }
  72. public Type getParameterType(int index)
  73. {
  74. if (! (argTypes instanceof Type[]))
  75. resolveParameterTypes();
  76. Type[] atypes = (Type[]) argTypes;
  77. if (index < atypes.length
  78. && (index < atypes.length - 1 || maxArgs() >= 0))
  79. return atypes[index];
  80. if (maxArgs() < 0)
  81. {
  82. Type rtype = atypes[atypes.length-1];
  83. if (rtype instanceof ArrayType)
  84. return ((ArrayType) rtype).getComponentType();
  85. }
  86. return Type.objectType;
  87. }
  88. /* Special code for matchState field to require exception on mismatch. */
  89. public static final int THROW_ON_EXCEPTION = 0;
  90. /** Return code from match: Unspecified failure. */
  91. public static final int NO_MATCH = -1;
  92. /** Return code from match: Too few actual arguments.
  93. * The lower half is the minimum number of arguments (if not 0xffff). */
  94. public static final int NO_MATCH_TOO_FEW_ARGS = 0xfff10000;
  95. /** Return code from match: Too many actual arguments.
  96. * The lower half is the maximum number of arguments (if not 0xffff). */
  97. public static final int NO_MATCH_TOO_MANY_ARGS = 0xfff20000;
  98. /** Return code from match: Ambigious which method to select. */
  99. public static final int NO_MATCH_AMBIGUOUS = 0xfff30000;
  100. /** Return code from match: Invalid argument type.
  101. * In that case the lower half is the 1-origin index of the first
  102. * argument that does not match. */
  103. public static final int NO_MATCH_BAD_TYPE = 0xfff40000;
  104. /** Return code from match: Unused keyword argument.
  105. * I.e. a keyword in the call doesn't match a keyword parameter.
  106. * In that case the lower half is the 1-origin index of the first
  107. * keyword argument that does not match. */
  108. public static final int NO_MATCH_UNUSED_KEYWORD = 0xfff50000;
  109. public static final int NO_MATCH_GUARD_FALSE = 0xfff60000;
  110. /** Helper method to throw an exception if a <code>matchX</code>
  111. * method fails. */
  112. public static RuntimeException
  113. matchFailAsException(int code, Procedure proc, ArgList args)
  114. {
  115. int arg = (short) code;
  116. code &= 0xffff0000;
  117. if (code != NO_MATCH_BAD_TYPE)
  118. return new WrongArguments(proc, args.numArguments());
  119. return new WrongType(proc, arg, arg > 0 ? args.getArgAsObject(arg-1) : null);
  120. }
  121. /** Return the more specific of the arguments.
  122. * @return null if neither is more specific. */
  123. public static MethodProc mostSpecific(MethodProc proc1, MethodProc proc2)
  124. {
  125. // True if we've determined proc1 cannot be the more specific. I.e. there
  126. // can be aguments lists that are applicable to proc1 and not proc2.
  127. boolean not1 = false;
  128. // True if we've determined proc2 cannot be the more specific.
  129. boolean not2 = false;
  130. int min1 = proc1.minArgs();
  131. int min2 = proc2.minArgs();
  132. int max1 = proc1.maxArgs();
  133. int max2 = proc2.maxArgs();
  134. if ((max1 >= 0 && max1 < min2)
  135. || (max2 >= 0 && max2 < min1))
  136. return null;
  137. int num1 = proc1.numParameters();
  138. int num2 = proc2.numParameters();
  139. int limit = num1 < num2 ? num1 : num2;
  140. if (max1 != max2)
  141. {
  142. if (max1 < 0)
  143. not1 = true;
  144. if (max2 < 0)
  145. not2 = true;
  146. }
  147. if (min1 < min2)
  148. not1 = true;
  149. else if (min1 > min2)
  150. not2 = true;
  151. for (int i = 0; i < limit; i++)
  152. {
  153. Type t1 = proc1.getParameterType(i);
  154. Type t2 = proc2.getParameterType(i);
  155. int c1 = t1.isCompatibleWithValue(t2);
  156. int c2 = t2.isCompatibleWithValue(t1);
  157. if (c2 >= 0 && c2 > c1)
  158. {
  159. not2 = true;
  160. if (not1)
  161. return null;
  162. }
  163. if (c1 >= 0 && c1 > c2)
  164. {
  165. not1 = true;
  166. if (not2)
  167. return null;
  168. }
  169. }
  170. return not2 ? proc1 : not1 ? proc2 : null;
  171. }
  172. /** An approximation of "override-equivalent" as defined in the JLS. */
  173. public static boolean overrideEquivalent(MethodProc proc1, MethodProc proc2) {
  174. int num1 = proc1.numParameters();
  175. int num2 = proc2.numParameters();
  176. if (num1 != num2)
  177. return false;
  178. for (int i = 1; i < num1; i++) {
  179. Type t1 = proc1.getParameterType(i);
  180. Type t2 = proc2.getParameterType(i);
  181. if (t1.compare(t2) != 0)
  182. return false;
  183. }
  184. return true;
  185. }
  186. public static Object applyToConsumerDefaultMP(Procedure proc, CallContext ctx) throws Throwable {
  187. throw new Error("applyToConsumerDefaultMP for "+proc+"::"+proc.getClass().getName());
  188. /*
  189. Object[] args = ctx.getArgs();
  190. System.err.println("applyToConsumerDefaultMP for "+proc+"::"+proc.getClass().getName());
  191. int code = proc.matchN(args, ctx);
  192. if (code != 0) {
  193. if (ctx.matchState == CallContext.MATCH_THROW_ON_EXCEPTION)
  194. throw MethodProc.matchFailAsException(code, proc, args);
  195. else
  196. return ctx;
  197. }
  198. //proc.apply(ctx);
  199. Object ignored = proc.applyToConsumerMethod.invokeExact(proc, ctx);
  200. return null;
  201. */
  202. }
  203. public static final MethodHandle applyToConsumerDefaultMP
  204. = lookupApplyHandle(MethodProc.class, "applyToConsumerDefaultMP");
  205. }