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