001: // Copyright (c) 1999 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: /** Match the incoming arguments.
065: * @param ctx where to save the matched result on success
066: * @param args the incoming argument list
067: * @return non-negative if the match succeeded, else negative
068: */
069: public abstract int match(CallContext ctx, Object[] args);
070:
071: // FUTURE:
072: // On success, vars has been initialized so vars.run() will work.
073: // public abstract int match(CallContext vars);
074:
075: /** Return code from match: Unspecified failure. */
076: public static final int NO_MATCH = -1;
077:
078: /** Return code from match: Too few actual arguments.
079: * The lower half is the minimum number of arguments (if not 0xffff). */
080: public static final int NO_MATCH_TOO_FEW_ARGS = 0xfff10000;
081:
082: /** Return code from match: Too many actual arguments.
083: * The lower half is the maximum number of arguments (if not 0xffff). */
084: public static final int NO_MATCH_TOO_MANY_ARGS = 0xfff20000;
085:
086: /** Return code from match: Ambigious which method to select. */
087: public static final int NO_MATCH_AMBIGUOUS = 0xfff30000;
088:
089: /** Return code from match: Invalid argument type.
090: * In that case the lower half is the 0-origin index of the first
091: * argument that does not match. */
092: public static final int NO_MATCH_BAD_TYPE = 0xfff40000;
093:
094: public Object match(Object[] args) {
095: CallContext ctx = new CallContext();
096: return match(ctx, args) == 0 ? ctx : null;
097: // FUTURE:
098: // vars.setArgs(args);
099: // return match(vars) == 0 ? vars : null;
100: }
101:
102: public abstract Object applyV(CallContext ctx) throws Throwable;
103:
104: // FUTURE:
105: // ctx.run();
106:
107: public static RuntimeException matchFailAsException(int code,
108: Procedure proc, Object[] args) {
109: int arg = (short) code;
110: code &= 0xffff0000;
111: if (code == NO_MATCH_TOO_FEW_ARGS
112: || code == NO_MATCH_TOO_MANY_ARGS)
113: return new WrongArguments(proc, args.length);
114: if (code != NO_MATCH_BAD_TYPE)
115: arg = WrongType.ARG_UNKNOWN;
116: throw new WrongType(proc, arg, null);
117: }
118:
119: public Object applyN(Object[] args) throws Throwable {
120: checkArgCount(this , args.length);
121: CallContext vars = new CallContext();
122: int err = match(vars, args);
123: if (err != 0)
124: throw matchFailAsException(err, this , args);
125: // FUTURE:
126: // vars.run();
127: // return vars.getResult();
128: return applyV(vars);
129: }
130:
131: /** Return the more specific of the arguments.
132: * @return null if neither is more specific. */
133: public static MethodProc mostSpecific(MethodProc proc1,
134: MethodProc proc2) {
135: // True if we've determined proc1 cannot be the more specific.
136: boolean not1 = false;
137: // True if we've determined proc2 cannot be the more specific.
138: boolean not2 = false;
139: int min1 = proc1.minArgs();
140: int min2 = proc2.minArgs();
141: int max1 = proc1.maxArgs();
142: int max2 = proc2.maxArgs();
143: int num1 = proc1.numParameters();
144: int num2 = proc2.numParameters();
145: int limit = num1 > num2 ? num1 : num2;
146: if (max1 != max2) {
147: if (max1 < 0)
148: not2 = true;
149: if (max2 < 0)
150: not1 = true;
151: }
152: if (min1 < min2)
153: not2 = true;
154: else if (min1 > min2)
155: not1 = true;
156: for (int i = 0; i < limit; i++) {
157: Type t1 = proc1.getParameterType(i);
158: Type t2 = proc2.getParameterType(i);
159: int comp = t1.compare(t2);
160: if (comp == -1) {
161: not2 = true;
162: if (not1)
163: return null;
164: }
165: if (comp == 1) {
166: not1 = true;
167: if (not2)
168: return null;
169: }
170: }
171: return not2 ? proc1 : not1 ? proc2 : null;
172: }
173:
174: /** Return the index of the most specific method. */
175: public static int mostSpecific(MethodProc[] procs, int length) {
176: MethodProc best = null;
177: if (length == 0)
178: return -1;
179: int result = 0;
180: best = procs[0];
181: for (int i = 1; i < length; i++) {
182: MethodProc method = procs[i];
183: best = mostSpecific(best, method);
184: if (best == null)
185: return -1;
186: else if (best == method)
187: result = i;
188: }
189: return result;
190: }
191: }
|