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