Procedure.java 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Copyright (c) 2004, 2015 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.mapping;
  4. /* #ifdef use:java.lang.invoke */
  5. import java.lang.invoke.*;
  6. /* #else */
  7. // import gnu.mapping.CallContext.MethodHandle;
  8. /* #endif */
  9. /**
  10. * The abstract parent for all Scheme functions.
  11. * @author Per Bothner
  12. */
  13. public class Procedure extends PropertySet
  14. {
  15. /** A static method with signature ??apply(Procedure,CallContext)
  16. */
  17. protected /*final?*/ MethodHandle applyToObjectMethod;
  18. protected /*final?*/ MethodHandle applyToConsumerMethod;
  19. ///** Does impemention write Object to Consumer?
  20. // * If so, implement applyToObjectMethod using applyToConsumerMethod.
  21. // * If false, impement applyToConsumerMethod using applyToObjectMethod.
  22. // */
  23. //protected boolean resultGoesToConsumer() {
  24. // return false;
  25. //}
  26. private static final String sourceLocationKey = "source-location";
  27. private static final Symbol setterKey = Namespace.EmptyNamespace.getSymbol("setter");
  28. /** Key for a property used by gnu.expr.Inlinecalls.
  29. * The property value is either a String of the form "CLASSNAME:METHODNAME",
  30. * or a java.lang.reflect.Method (or FUTURE: MethodHandle) for a static
  31. * method whose parameters are
  32. * {@code (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)} and returns a re-written/validated {@code Expression}.
  33. */
  34. public static final Symbol validateApplyKey =
  35. Namespace.EmptyNamespace.getSymbol("validate-apply");
  36. /** Same as validateApplyKey but handles splice args. */
  37. public static final Symbol validateXApplyKey =
  38. Namespace.EmptyNamespace.getSymbol("validate-xapply");
  39. public static final Symbol compilerXKey =
  40. Namespace.EmptyNamespace.getSymbol("compile-apply");
  41. public static final Symbol inlineIfConstantSymbol =
  42. Namespace.EmptyNamespace.getSymbol("inline-if-constant");
  43. // This should be a LazyPropertyKey<gnu.expr.Inlineable>, but we want
  44. // to avoid any strict dependency on gnu.expr for run-time classes.
  45. public static final LazyPropertyKey<?> compilerKey
  46. = new LazyPropertyKey("compiler");
  47. public void setSourceLocation (String file, int line)
  48. {
  49. setProperty(sourceLocationKey, file + ":" + line);
  50. }
  51. public String getSourceLocation ()
  52. {
  53. Object value = getProperty(sourceLocationKey, null);
  54. return value == null ? null : value.toString();
  55. }
  56. public final MethodHandle getApplyToConsumerMethod() {
  57. return applyToConsumerMethod;
  58. }
  59. public final MethodHandle getApplyToObjectMethod() {
  60. return applyToObjectMethod;
  61. }
  62. public static Object applyToConsumerDefault(Procedure proc, CallContext ctx) throws Throwable {
  63. ctx.proc = proc;
  64. Object r = proc.applyToObjectMethod.invokeExact(proc, ctx);
  65. if (r != ctx) {
  66. Values.writeValues(r, ctx.consumer);
  67. r = null;
  68. }
  69. return r;
  70. }
  71. public static Object applyToObjectDefault(Procedure proc, CallContext ctx)
  72. throws Throwable {
  73. int start = ctx.startFromContext();
  74. try {
  75. if (proc.applyToConsumerMethod.invokeExact(proc, ctx) != ctx)
  76. return ctx.getFromContext(start);
  77. ctx.cleanupFromContext(start);
  78. return ctx;
  79. //Object v = ctx.getFromContext(start);
  80. //return r == ctx ? null : v;
  81. } catch (Throwable ex) {
  82. ctx.cleanupFromContext(start);
  83. throw ex;
  84. }
  85. //return ctx.runUntilValue();
  86. }
  87. public Procedure() {
  88. }
  89. public Procedure(String n) {
  90. setName(n);
  91. }
  92. public Procedure(boolean resultGoesToConsumer, MethodHandle applyMethod) {
  93. if (resultGoesToConsumer) {
  94. applyToConsumerMethod = applyMethod;
  95. applyToObjectMethod = applyToObjectDefault;
  96. } else {
  97. applyToObjectMethod = applyMethod;
  98. applyToConsumerMethod = applyToConsumerDefault;
  99. }
  100. }
  101. public Procedure(boolean resultGoesToConsumer, MethodHandle applyMethod, String n) {
  102. this(resultGoesToConsumer, applyMethod);
  103. setName(n);
  104. }
  105. public void checkBadCode(CallContext ctx) {
  106. int code = 0; // FIXME
  107. //throw MethodProc.matchFailAsException(code, this, args);
  108. }
  109. public Object applyL(ArgList args) throws Throwable {
  110. CallContext ctx = CallContext.getInstance();
  111. ctx.setupApply(this);
  112. ctx.addAll(args);
  113. return ctx.runUntilValue();
  114. }
  115. public Object applyN (Object[] args) throws Throwable {
  116. CallContext ctx = CallContext.getInstance();
  117. ctx.setupApplyAll(this, args);
  118. return ctx.runUntilValue();
  119. }
  120. public Object apply0() throws Throwable {
  121. CallContext ctx = CallContext.getInstance();
  122. ctx.setupApply(this);
  123. return ctx.runUntilValue();
  124. }
  125. public Object apply1(Object arg1) throws Throwable {
  126. CallContext ctx = CallContext.getInstance();
  127. ctx.setupApply(this, arg1);
  128. return ctx.runUntilValue();
  129. }
  130. public Object apply2(Object arg1,Object arg2) throws Throwable {
  131. CallContext ctx = CallContext.getInstance();
  132. ctx.setupApply(this, arg1, arg2);
  133. return ctx.runUntilValue();
  134. }
  135. public Object apply3(Object arg1, Object arg2,
  136. Object arg3) throws Throwable {
  137. CallContext ctx = CallContext.getInstance();
  138. ctx.setupApply(this, arg1, arg2, arg3);
  139. return ctx.runUntilValue();
  140. }
  141. public Object apply4(Object arg1, Object arg2,
  142. Object arg3, Object arg4) throws Throwable {
  143. CallContext ctx = CallContext.getInstance();
  144. ctx.setupApply(this, arg1, arg2, arg3, arg4);
  145. return ctx.runUntilValue();
  146. }
  147. /** Minimum number of arguments required. */
  148. public final int minArgs() { return minArgs(numArgs()); }
  149. /** Maximum number of arguments allowed, or -1 for unlimited.
  150. * (May also return -1 if there are keyword arguments, for implementation
  151. * reasons - FIXME.) */
  152. public final int maxArgs() { return maxArgs(numArgs()); }
  153. /** Return {@code minArgs()|(maxArgs<<12)}.
  154. * We use a single virtual function to reduce the number of methods
  155. * in the system, as well as the number of virtual method table entries.
  156. * We shift by 12 so the number can normally be represented using a
  157. * sipush instruction, without requiring a constant pool entry.
  158. */
  159. public int numArgs() { return 0xfffff000; }
  160. /** Extract minimum number of arguments from {@code numArgs()} encoding. */
  161. public static int minArgs (int num) { return num & 0xFFF; }
  162. /** Extract maximum number of arguments from {@code numArgs()} encoding. */
  163. public static int maxArgs (int num) { return num >> 12; }
  164. /** Check that the number of arguments in a call is valid.
  165. * @param proc the Procedure being called
  166. * @param argCount the number of arguments in the call
  167. * @exception WrongArguments there are too many or too
  168. * few actual arguments
  169. */
  170. public static void checkArgCount(Procedure proc, int argCount)
  171. {
  172. int num = proc.numArgs();
  173. if (argCount < minArgs(num)
  174. || (num >= 0 && argCount > maxArgs(num)))
  175. throw new WrongArguments(proc, argCount);
  176. }
  177. public Procedure getSetter()
  178. {
  179. if (! (this instanceof HasSetter))
  180. {
  181. Object setter = getProperty(setterKey, null);
  182. if (setter instanceof Procedure)
  183. return (Procedure) setter;
  184. throw new RuntimeException("procedure '"+getName()+ "' has no setter");
  185. }
  186. int num_args = numArgs();
  187. if (num_args == 0x0000)
  188. return new Setter0(this);
  189. if (num_args == 0x1001)
  190. return new Setter1(this);
  191. return new Setter(this);
  192. }
  193. public void setSetter (Procedure setter)
  194. {
  195. if (this instanceof HasSetter)
  196. throw new RuntimeException("procedure '"+getName()+
  197. "' has builtin setter - cannot be modified");
  198. setProperty(Procedure.setterKey, setter);
  199. }
  200. /** If HasSetter, the Procedure is called in the LHS of an assignment. */
  201. public void set0(Object result) throws Throwable
  202. {
  203. getSetter().apply1(result);
  204. }
  205. public void set1(Object arg1, Object value) throws Throwable
  206. {
  207. getSetter().apply2(arg1, value);
  208. }
  209. public void setN (Object[] args) throws Throwable
  210. {
  211. getSetter().applyN(args);
  212. }
  213. /** True if this Procedure (definitely) has no side-effects.
  214. * Note side-effect-free does not imply idempotent if this
  215. * allocates an object with "identity".
  216. */
  217. public boolean isSideEffectFree ()
  218. {
  219. return false;
  220. }
  221. /** Semi-deprecated - instead should be set at Inline time. FIXME */
  222. public gnu.bytecode.Type getReturnType (gnu.expr.Expression[] args)
  223. {
  224. return gnu.bytecode.Type.objectType;
  225. }
  226. public String toString ()
  227. {
  228. StringBuffer sbuf = new StringBuffer();
  229. sbuf.append ("#<procedure ");
  230. String n = getName();
  231. if (n == null)
  232. n = getSourceLocation();
  233. if (n == null)
  234. n = getClass().getName();
  235. sbuf.append(n);
  236. sbuf.append('>');
  237. return sbuf.toString();
  238. }
  239. public static MethodHandle lookupApplyHandle(Class clas, String mname) {
  240. try {
  241. /* #ifdef use:java.lang.invoke */
  242. return MethodHandles.lookup()
  243. .findStatic(clas, mname, applyMethodType);
  244. /* #else */
  245. // return new CallContext.ReflectMethodHandle(clas.getDeclaredMethod(mname, applyMethodType));
  246. /* #endif */
  247. } catch (Exception ex) {
  248. throw new RuntimeException(ex);
  249. }
  250. }
  251. /* #ifdef use:java.lang.invoke */
  252. public static final MethodType applyMethodType =
  253. MethodType.methodType(Object.class, Procedure.class, CallContext.class);
  254. /* #else */
  255. // public static final Class[] applyMethodType =
  256. // { Procedure.class, CallContext.class };
  257. /* #endif */
  258. public static final MethodHandle applyToObjectDefault
  259. = lookupApplyHandle(Procedure.class, "applyToObjectDefault");
  260. public static final MethodHandle applyToConsumerDefault
  261. = lookupApplyHandle(Procedure.class, "applyToConsumerDefault");
  262. }