001: // Copyright (c) 1999, 2004 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.mapping;
005:
006: import gnu.bytecode.Type;
007:
008: /** Similar to a CLOS method.
009: * Can check if arguments "match" before committing to calling method. */
010:
011: public abstract class MethodProc extends ProcedureN {
012: /** The parameter types.
013: * Usually either an Type[] or a String encoding. */
014: protected Object argTypes;
015:
016: /** Test if method is applicable to an invocation with given arguments.
017: * Returns -1 if no; 1 if yes; 0 if need to check at run-time. */
018: public int isApplicable(Type[] argTypes) {
019: int argCount = argTypes.length;
020: int num = numArgs();
021: if (argCount < (num & 0xFFF)
022: || (num >= 0 && argCount > (num >> 12)))
023: return -1;
024: int result = 1;
025: for (int i = argCount; --i >= 0;) {
026: Type ptype = getParameterType(i);
027: int code = ptype.compare(argTypes[i]);
028: if (code == -3)
029: return -1;
030: if (code < 0)
031: result = 0;
032: }
033: return result;
034: }
035:
036: /** Return number of parameters, including optional and rest arguments. */
037: public int numParameters() {
038: int num = numArgs();
039: int max = num >> 12;
040: if (max >= 0)
041: return max;
042: // This isn't really right, but it works for PrimProcedure. FIXME.
043: int min = num & 0xFFF;
044: return min + 1;
045: }
046:
047: static final Type[] unknownArgTypes = { Type.pointer_type };
048:
049: /** Figure out or decode the parameter types, setting argTypes. */
050: protected void resolveParameterTypes() {
051: argTypes = unknownArgTypes;
052: }
053:
054: public Type getParameterType(int index) {
055: if (!(argTypes instanceof Type[]))
056: resolveParameterTypes();
057:
058: Type[] atypes = (Type[]) argTypes;
059: if (index >= atypes.length)
060: index = atypes.length - 1;
061: return atypes[index];
062: }
063:
064: /** Return code from match: Unspecified failure. */
065: public static final int NO_MATCH = -1;
066:
067: /** Return code from match: Too few actual arguments.
068: * The lower half is the minimum number of arguments (if not 0xffff). */
069: public static final int NO_MATCH_TOO_FEW_ARGS = 0xfff10000;
070:
071: /** Return code from match: Too many actual arguments.
072: * The lower half is the maximum number of arguments (if not 0xffff). */
073: public static final int NO_MATCH_TOO_MANY_ARGS = 0xfff20000;
074:
075: /** Return code from match: Ambigious which method to select. */
076: public static final int NO_MATCH_AMBIGUOUS = 0xfff30000;
077:
078: /** Return code from match: Invalid argument type.
079: * In that case the lower half is the 1-origin index of the first
080: * argument that does not match. */
081: public static final int NO_MATCH_BAD_TYPE = 0xfff40000;
082:
083: /** Helper method to throw an exception if a <code>matchX</code>
084: * method fails. */
085: public static RuntimeException matchFailAsException(int code,
086: Procedure proc, Object[] args) {
087: int arg = (short) code;
088: code &= 0xffff0000;
089: if (code != NO_MATCH_BAD_TYPE)
090: return new WrongArguments(proc, args.length);
091: return new WrongType(proc, arg, arg > 0 ? args[arg - 1] : null);
092: }
093:
094: public Object applyN(Object[] args) throws Throwable {
095: checkArgCount(this , args.length);
096: CallContext ctx = CallContext.getInstance();
097: checkN(args, ctx);
098: return ctx.runUntilValue();
099: }
100:
101: /** Return the more specific of the arguments.
102: * @return null if neither is more specific. */
103: public static MethodProc mostSpecific(MethodProc proc1,
104: MethodProc proc2) {
105: // True if we've determined proc1 cannot be the more specific.
106: boolean not1 = false;
107: // True if we've determined proc2 cannot be the more specific.
108: boolean not2 = false;
109: int min1 = proc1.minArgs();
110: int min2 = proc2.minArgs();
111: int max1 = proc1.maxArgs();
112: int max2 = proc2.maxArgs();
113: if ((max1 >= 0 && max1 < min2) || (max2 >= 0 && max2 < min1))
114: return null;
115: int num1 = proc1.numParameters();
116: int num2 = proc2.numParameters();
117: int limit = num1 > num2 ? num1 : num2;
118: if (max1 != max2) {
119: if (max1 < 0)
120: not2 = true;
121: if (max2 < 0)
122: not1 = true;
123: }
124: if (min1 < min2)
125: not2 = true;
126: else if (min1 > min2)
127: not1 = true;
128: for (int i = 0; i < limit; i++) {
129: Type t1 = proc1.getParameterType(i);
130: Type t2 = proc2.getParameterType(i);
131: int comp = t1.compare(t2);
132: if (comp == -1) {
133: not2 = true;
134: if (not1)
135: return null;
136: }
137: if (comp == 1) {
138: not1 = true;
139: if (not2)
140: return null;
141: }
142: }
143: return not2 ? proc1 : not1 ? proc2 : null;
144: }
145:
146: /** Return the index of the most specific method. */
147: public static int mostSpecific(MethodProc[] procs, int length) {
148: if (length <= 1) // Handles length==0 and length==1.
149: return length - 1;
150: // best is non-null if there is a single most specific method.
151: MethodProc best = procs[0];
152: // This array (which is allocated lazily) is used if there is is a set
153: // of bestn methods none of which are more specific than the others.
154: MethodProc[] bests = null;
155: // If best==null, then the index of the most specific method;
156: // otherwise the active length of the bests array.
157: int bestn = 0;
158: outer: for (int i = 1; i < length; i++) {
159: MethodProc method = procs[i];
160: if (best != null) {
161: MethodProc winner = mostSpecific(best, method);
162: if (winner == null) {
163: if (bests == null)
164: bests = new MethodProc[length];
165: bests[0] = best;
166: bests[1] = method;
167: bestn = 2;
168: best = null;
169: } else if (winner == method) {
170: best = method;
171: bestn = i;
172: }
173: } else {
174: for (int j = 0; j < bestn; j++) {
175: MethodProc old = bests[j];
176: MethodProc winner = mostSpecific(old, method);
177: if (winner == old)
178: continue outer;
179: if (winner == null) {
180: bests[bestn++] = method;
181: continue outer;
182: }
183: }
184: // At this point method is more specific than bests[0..bestn-1].
185: best = method;
186: bestn = i;
187: }
188: }
189: return best == null ? -1 : bestn;
190: }
191: }
|