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