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