001: package gnu.kawa.reflect;
002:
003: import gnu.mapping.*;
004: import gnu.expr.*;
005: import gnu.bytecode.*;
006: import gnu.lists.FString;
007: import java.lang.reflect.Array;
008: import gnu.kawa.lispexpr.ClassNamespace; // FIXME
009:
010: public class Invoke extends ProcedureN implements CanInline {
011: /** The kind on invoke operation.
012: * 'N' - make (new).
013: * 'S' - invoke-static (static or non-static):
014: * The first operand is a Class or Type, the second is the name,
015: * and if the is non-static the 3rd is the receiver.
016: * 's' - Like 'S' but only allow static methods. [not used]
017: * 'V' - non-static invoke, only allow non-static methods. [not used]
018: * '*' - non-static invoke, can match static methods also.
019: * This is Java's 'Primary.MethodName(args)' - if the selected method
020: * is static, we only use Primary's type for method select,
021: * but ignore its value.
022: */
023: char kind;
024:
025: Language language;
026:
027: public static final Invoke invoke = new Invoke("invoke", '*');
028: public static final Invoke invokeStatic = new Invoke(
029: "invoke-static", 'S');
030: public static final Invoke invokeSpecial = new Invoke(
031: "invoke-special", 'P');
032: public static final Invoke make = new Invoke("make", 'N');
033:
034: public Invoke(String name, char kind) {
035: super (name);
036: this .kind = kind;
037: this .language = Language.getDefaultLanguage();
038: }
039:
040: public Invoke(String name, char kind, Language language) {
041: super (name);
042: this .kind = kind;
043: this .language = language;
044: }
045:
046: public static Object invoke$V(Object[] args) throws Throwable {
047: return invoke.applyN(args);
048: }
049:
050: public static Object invokeStatic$V(Object[] args) throws Throwable {
051: return invokeStatic.applyN(args);
052: }
053:
054: public static Object make$V(Object[] args) throws Throwable {
055: return make.applyN(args);
056: }
057:
058: private static ObjectType typeFrom(Object arg, Invoke this Proc) {
059: if (arg instanceof Class)
060: arg = Type.make((Class) arg);
061: if (arg instanceof ObjectType)
062: return (ObjectType) arg;
063: if (arg instanceof String || arg instanceof FString)
064: return ClassType.make(arg.toString());
065: if (arg instanceof Symbol)
066: return ClassType.make(((Symbol) arg).getName());
067: if (arg instanceof ClassNamespace)
068: return ((ClassNamespace) arg).getClassType();
069: throw new WrongType(this Proc, 0, arg, "class-specifier");
070: }
071:
072: public void apply(CallContext ctx) throws Throwable {
073: Object[] args = ctx.getArgs();
074: if (kind == 'S' || kind == 'V' || kind == 's' || kind == '*') {
075: // The following is an optimization, so that output from the
076: // method is sent directly to ctx.consumer, rather than reified.
077: int nargs = args.length;
078: Procedure.checkArgCount(this , nargs);
079: Object arg0 = args[0];
080: ObjectType dtype = (ObjectType) ((kind == 'S' || kind == 's') ? typeFrom(
081: arg0, this )
082: : Type.make(arg0.getClass()));
083: Procedure proc = lookupMethods(dtype, args[1]);
084: Object[] margs = new Object[nargs - (kind == 'S' ? 2 : 1)];
085: int i = 0;
086: if (kind == 'V' || kind == '*')
087: margs[i++] = args[0];
088: System.arraycopy(args, 2, margs, i, nargs - 2);
089: proc.checkN(margs, ctx);
090: } else
091: ctx.writeValue(this .applyN(args));
092: }
093:
094: public Object applyN(Object[] args) throws Throwable {
095: if (kind == 'P')
096: throw new RuntimeException(getName()
097: + ": invoke-special not allowed at run time");
098:
099: int nargs = args.length;
100: Procedure.checkArgCount(this , nargs);
101: Object arg0 = args[0];
102: ObjectType dtype = (kind != 'V' && kind != '*' ? typeFrom(arg0,
103: this ) : (ObjectType) Type.make(arg0.getClass()));
104: Object mname;
105: if (kind == 'N') {
106: mname = null;
107: if (dtype instanceof TypeValue) {
108: Procedure constructor = ((TypeValue) dtype)
109: .getConstructor();
110: if (constructor != null) {
111: nargs--;
112: Object[] xargs = new Object[nargs];
113: System.arraycopy(args, 1, xargs, 0, nargs);
114: return constructor.applyN(xargs);
115: }
116: }
117: if (dtype instanceof PairClassType) {
118: PairClassType ptype = (PairClassType) dtype;
119: dtype = ptype.instanceType;
120: }
121: if (dtype instanceof ArrayType) {
122: Type elementType = ((ArrayType) dtype)
123: .getComponentType();
124: int len;
125: len = args.length - 1;
126: String name;
127: int length;
128: int i;
129: boolean lengthSpecified;
130: if (len >= 2
131: && args[1] instanceof Keyword
132: && ("length".equals(name = ((Keyword) args[1])
133: .getName()) || "size".equals(name))) {
134: length = ((Number) args[2]).intValue();
135: i = 3;
136: lengthSpecified = true;
137: } else {
138: length = len;
139: i = 1;
140: lengthSpecified = false;
141: }
142: Object arr = Array.newInstance(elementType
143: .getReflectClass(), length);
144: int index = 0;
145: for (; i <= len; i++) {
146: Object arg = args[i];
147: if (lengthSpecified && arg instanceof Keyword
148: && i < len) {
149: String kname = ((Keyword) arg).getName();
150: try {
151: index = Integer.parseInt(kname);
152: } catch (Throwable ex) {
153: throw new RuntimeException(
154: "non-integer keyword '" + kname
155: + "' in array constructor");
156: }
157: arg = args[++i];
158: }
159: Array.set(arr, index, elementType
160: .coerceFromObject(arg));
161: index++;
162: }
163: return arr;
164: }
165: } else {
166: mname = args[1];
167: }
168: MethodProc proc = lookupMethods((ObjectType) dtype, mname);
169: if (kind != 'N') {
170: Object[] margs = new Object[nargs
171: - (kind == 'S' || kind == 's' ? 2 : 1)];
172: int i = 0;
173: if (kind == 'V' || kind == '*')
174: margs[i++] = args[0];
175: System.arraycopy(args, 2, margs, i, nargs - 2);
176: return proc.applyN(margs);
177: } else {
178: CallContext vars = CallContext.getInstance();
179: int err = proc.matchN(args, vars);
180: if (err == 0)
181: return vars.runUntilValue();
182:
183: if ((nargs & 1) == 1) {
184: // Check if args is a set of (keyword,value)-pairs.
185: for (int i = 1;; i += 2) {
186: if (i == nargs) {
187: Object result;
188: result = proc.apply1(args[0]);
189: for (i = 1; i < nargs; i += 2) {
190: Keyword key = (Keyword) args[i];
191: Object arg = args[i + 1];
192: SlotSet.apply(false, result, key.getName(),
193: arg);
194: }
195: return result;
196: }
197: if (!(args[i] instanceof Keyword))
198: break;
199: }
200: }
201: MethodProc vproc = ClassMethods.apply((ClassType) dtype,
202: "valueOf", '\0', language);
203: if (vproc != null) {
204: Object[] margs = new Object[nargs - 1];
205: System.arraycopy(args, 1, margs, 0, nargs - 1);
206: err = vproc.matchN(margs, vars);
207: if (err == 0)
208: return vars.runUntilValue();
209: }
210:
211: throw MethodProc.matchFailAsException(err, proc, args);
212: }
213: }
214:
215: public int numArgs() {
216: return (-1 << 12) | (kind == 'N' ? 1 : 2);
217: }
218:
219: protected MethodProc lookupMethods(ObjectType dtype, Object name) {
220: String mname;
221: if (kind == 'N')
222: mname = "<init>";
223: else {
224: if (name instanceof String || name instanceof FString)
225: mname = name.toString();
226: else if (name instanceof Symbol)
227: mname = ((Symbol) name).getName();
228: else
229: throw new WrongType(this , 1, null);
230: mname = Compilation.mangleName(mname);
231: }
232: MethodProc proc = ClassMethods.apply(dtype, mname,
233: kind == 'P' ? 'P' : kind == '*' || kind == 'V' ? 'V'
234: : '\0', language);
235: if (proc == null)
236: throw new RuntimeException(getName()
237: + ": no method named `" + mname + "' in class "
238: + dtype.getName());
239: return proc;
240: }
241:
242: protected PrimProcedure[] getMethods(ObjectType ctype,
243: String mname, ClassType caller) {
244: return ClassMethods.getMethods(ctype, mname, kind == 'P' ? 'P'
245: : kind == '*' || kind == 'V' ? 'V' : '\0', caller,
246: language);
247: }
248:
249: private static long selectApplicable(PrimProcedure[] methods,
250: ObjectType ctype, Expression[] args, int margsLength,
251: int argsStartIndex, int objIndex) {
252: Type[] atypes = new Type[margsLength];
253:
254: int dst = 0;
255: if (objIndex >= 0)
256: atypes[dst++] = ctype;
257: for (int src = argsStartIndex; src < args.length
258: && dst < atypes.length; src++, dst++)
259: atypes[dst] = args[src].getType();
260: return ClassMethods.selectApplicable(methods, atypes);
261: }
262:
263: /** Return an array if args (starting with start) is a set of
264: * (keyword, value)-value pairs. */
265: static Object[] checkKeywords(Type type, Expression[] args,
266: int start, ClassType caller) {
267: int len = args.length;
268: if (((len - start) & 1) != 0)
269: return null;
270: Object[] fields = new Object[(len - start) >> 1];
271: for (int i = fields.length; --i >= 0;) {
272: Expression arg = args[start + 2 * i];
273: if (!(arg instanceof QuoteExp))
274: return null;
275: Object value = ((QuoteExp) arg).getValue();
276: if (!(value instanceof Keyword))
277: return null;
278: String name = ((Keyword) value).getName();
279: Member slot = SlotSet.lookupMember((ClassType) type, name,
280: caller);
281: fields[i] = slot != null ? (Object) slot : (Object) name;
282: }
283: return fields;
284: }
285:
286: /** Check if class exists.
287: * @return 1 if class actually exists;
288: * -1 is class should exist, but doesn't;
289: * and 0 otherwise.
290: */
291: public static int checkKnownClass(Type type, Compilation comp) {
292: if (type instanceof ClassType
293: && ((ClassType) type).isExisting()) {
294: try {
295: type.getReflectClass();
296: return 1;
297: } catch (Exception ex) {
298: comp.error('e', "unknown class: " + type.getName());
299: return -1;
300: }
301: }
302: return 0;
303: }
304:
305: /** Resolve class specifier to ClassType at inline time.
306: * This is an optimization to avoid having a module-level binding
307: * created for the class name. */
308:
309: public static ApplyExp inlineClassName(ApplyExp exp, int carg,
310: InlineCalls walker) {
311: Compilation comp = walker.getCompilation();
312: Language language = comp.getLanguage();
313: Expression[] args = exp.getArgs();
314: if (args.length > carg) {
315: Type type = language.getTypeFor(args[carg]);
316: if (!(type instanceof Type))
317: return exp;
318: checkKnownClass(type, comp);
319: Expression[] nargs = new Expression[args.length];
320: System.arraycopy(args, 0, nargs, 0, args.length);
321: nargs[carg] = new QuoteExp(type);
322: ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs);
323: nexp.setLine(exp);
324: return nexp;
325: }
326: return exp;
327: }
328:
329: public Expression inline(ApplyExp exp, ExpWalker walker) {
330: Compilation comp = walker.getCompilation();
331: Expression[] args = exp.getArgs();
332: int nargs = args.length;
333: if (!comp.mustCompile
334: // This should never happen, as InlineCalls.walkApplyExp
335: // checks the number of arguments before inline is called.
336: || nargs == 0
337: || ((kind == 'V' || kind == '*') && nargs == 1))
338: return exp;
339: ObjectType type;
340: Expression arg0 = args[0];
341: Type type0 = (kind == 'V' || kind == '*' ? arg0.getType()
342: : language.getTypeFor(arg0));
343: if (type0 instanceof PairClassType)
344: type = ((PairClassType) type0).instanceType;
345: else if (type0 instanceof ObjectType)
346: type = (ObjectType) type0;
347: else
348: type = null;
349: String name = getMethodName(args);
350:
351: int margsLength, argsStartIndex, objIndex;
352: if (kind == 'V' || kind == '*') // Invoke virtual
353: {
354: margsLength = nargs - 1;
355: argsStartIndex = 2;
356: objIndex = 0;
357: } else if (kind == 'N') // make new
358: {
359: margsLength = nargs;
360: argsStartIndex = 0;
361: objIndex = -1;
362: } else if (kind == 'S' || kind == 's') // Invoke static
363: {
364: margsLength = nargs - 2;
365: argsStartIndex = 2;
366: objIndex = -1;
367: } else if (kind == 'P') // Invoke special
368: {
369: margsLength = nargs - 2;
370: argsStartIndex = 3;
371: objIndex = 1;
372: } else
373: return exp;
374:
375: if (kind == 'N' && type instanceof ArrayType) {
376: ArrayType atype = (ArrayType) type;
377: Type elementType = atype.getComponentType();
378: Expression sizeArg = null;
379: boolean lengthSpecified = false;
380: if (args.length >= 3 && args[1] instanceof QuoteExp) {
381: Object arg1 = ((QuoteExp) args[1]).getValue();
382: if (arg1 instanceof Keyword
383: && ("length".equals(name = ((Keyword) arg1)
384: .getName()) || "size".equals(name))) {
385: sizeArg = args[2];
386: lengthSpecified = true;
387: }
388: }
389: if (sizeArg == null)
390: sizeArg = QuoteExp.getInstance(new Integer(
391: args.length - 1));
392: Expression alloc = new ApplyExp(new ArrayNew(elementType),
393: new Expression[] { sizeArg });
394: if (lengthSpecified && args.length == 3)
395: return alloc;
396: LetExp let = new LetExp(new Expression[] { alloc });
397: Declaration adecl = let
398: .addDeclaration((String) null, atype);
399: adecl.noteValue(alloc);
400: BeginExp begin = new BeginExp();
401: int index = 0;
402: for (int i = lengthSpecified ? 3 : 1; i < args.length; i++) {
403: Expression arg = args[i];
404: if (lengthSpecified && i + 1 < args.length
405: && arg instanceof QuoteExp) {
406: Object key = ((QuoteExp) arg).getValue();
407: if (key instanceof Keyword) {
408: String kname = ((Keyword) key).getName();
409: try {
410: index = Integer.parseInt(kname);
411: arg = args[++i];
412: } catch (Throwable ex) {
413: comp.error('e', "non-integer keyword '"
414: + kname + "' in array constructor");
415: return exp;
416: }
417: }
418: }
419: begin
420: .add(new ApplyExp(
421: new ArraySet(elementType),
422: new Expression[] {
423: new ReferenceExp(adecl),
424: QuoteExp
425: .getInstance(new Integer(
426: index)), arg }));
427: index++;
428: }
429: begin.add(new ReferenceExp(adecl));
430: let.body = begin;
431: return let;
432: } else if (type != null && name != null) {
433: if (type instanceof TypeValue && kind == 'N') {
434: Procedure constructor = ((TypeValue) type)
435: .getConstructor();
436: if (constructor != null) {
437: Expression[] xargs = new Expression[nargs - 1];
438: System.arraycopy(args, 1, xargs, 0, nargs - 1);
439: return ((InlineCalls) walker)
440: .walkApplyOnly(new ApplyExp(constructor,
441: xargs));
442: }
443: }
444: PrimProcedure[] methods;
445: int okCount, maybeCount;
446: ClassType caller = comp == null ? null
447: : comp.curClass != null ? comp.curClass
448: : comp.mainClass;
449: ObjectType ctype = (ObjectType) type;
450: try {
451: methods = getMethods(ctype, name, caller);
452: long num = selectApplicable(methods, ctype, args,
453: margsLength, argsStartIndex, objIndex);
454: okCount = (int) (num >> 32);
455: maybeCount = (int) num;
456: } catch (Exception ex) {
457: comp.error('w', "unknown class: " + type.getName());
458: return exp;
459: }
460: int index = -1;
461: Object[] slots;
462: if (okCount + maybeCount == 0
463: && kind == 'N'
464: && (ClassMethods.selectApplicable(methods,
465: new Type[] { Compilation.typeClassType }) >> 32) == 1
466: && (slots = checkKeywords(type, args, 1, caller)) != null) {
467: StringBuffer errbuf = null;
468: for (int i = 0; i < slots.length; i++) {
469: if (slots[i] instanceof String) {
470: if (errbuf == null) {
471: errbuf = new StringBuffer();
472: errbuf.append("no field or setter ");
473: } else
474: errbuf.append(", ");
475: errbuf.append('`');
476: errbuf.append(slots[i]);
477: errbuf.append('\'');
478: }
479: }
480: if (errbuf != null) {
481: errbuf.append(" in class ");
482: errbuf.append(type.getName());
483: comp.error('w', errbuf.toString());
484: return exp;
485: } else {
486: ApplyExp e = new ApplyExp(methods[0],
487: new Expression[] { arg0 });
488: for (int i = 0; i < slots.length; i++) {
489: Expression[] sargs = { e,
490: new QuoteExp(slots[i]), args[2 * i + 2] };
491: e = new ApplyExp(SlotSet.setFieldReturnObject,
492: sargs);
493: }
494: return e.setLine(exp);
495: }
496: }
497: int nmethods = methods.length;
498: if (okCount + maybeCount == 0 && kind == 'N') {
499: methods = invokeStatic.getMethods(ctype, "valueOf",
500: caller);
501: argsStartIndex = 1;
502: margsLength = nargs - 1;
503: long num = selectApplicable(methods, ctype, args,
504: margsLength, argsStartIndex, -1);
505: okCount = (int) (num >> 32);
506: maybeCount = (int) num;
507: }
508: if (okCount + maybeCount == 0) {
509: if (comp.getBooleanOption("warn-invoke-unknown-method",
510: true)) {
511: if (kind == 'N')
512: name = name + "/valueOf";
513: if (nmethods + methods.length == 0)
514: comp.error('w', "no accessible method '" + name
515: + "' in " + type.getName());
516: else
517: comp.error('w',
518: "no possibly applicable method '"
519: + name + "' in "
520: + type.getName());
521: }
522: } else if (okCount == 1
523: || (okCount == 0 && maybeCount == 1))
524: index = 0;
525: else if (okCount > 0) {
526: index = MethodProc.mostSpecific(methods, okCount);
527: if (index < 0) {
528: if (kind == 'S') {
529: // If we didn't find a most specific method,
530: // check if there is one that is static. If so,
531: // prefer that - after all, we're using invoke-static.
532: for (int i = 0; i < okCount; i++) {
533: if (methods[i].getStaticFlag()) {
534: if (index >= 0) {
535: index = -1;
536: break;
537: } else
538: index = i;
539: }
540: }
541: }
542: }
543: if (index < 0
544: && comp.getBooleanOption(
545: "warn-invoke-unknown-method", true)) {
546: StringBuffer sbuf = new StringBuffer();
547: sbuf
548: .append("more than one definitely applicable method `");
549: sbuf.append(name);
550: sbuf.append("' in ");
551: sbuf.append(type.getName());
552: append(methods, okCount, sbuf);
553: comp.error('w', sbuf.toString());
554: }
555: } else if (comp.getBooleanOption(
556: "warn-invoke-unknown-method", true)) {
557: StringBuffer sbuf = new StringBuffer();
558: sbuf
559: .append("more than one possibly applicable method '");
560: sbuf.append(name);
561: sbuf.append("' in ");
562: sbuf.append(type.getName());
563: append(methods, maybeCount, sbuf);
564: comp.error('w', sbuf.toString());
565: }
566: if (index >= 0) {
567: Expression[] margs = new Expression[margsLength];
568: int dst = 0;
569: if (objIndex >= 0)
570: margs[dst++] = args[objIndex];
571: for (int src = argsStartIndex; src < args.length
572: && dst < margs.length; src++, dst++)
573: margs[dst] = args[src];
574: return new ApplyExp(methods[index], margs).setLine(exp);
575: }
576: }
577: return exp;
578: }
579:
580: private void append(PrimProcedure[] methods, int mcount,
581: StringBuffer sbuf) {
582: for (int i = 0; i < mcount; i++) {
583: sbuf.append("\n candidate: ");
584: sbuf.append(methods[i]);
585: }
586: }
587:
588: private String getMethodName(Expression[] args) {
589: if (kind == 'N')
590: return "<init>";
591: int nameIndex = (kind == 'P' ? 2 : 1);
592: if (args.length >= nameIndex + 1)
593: return ClassMethods.checkName(args[nameIndex], false);
594: return null;
595: }
596:
597: /** Return an ApplyExp that will call a method with given arguments.
598: * @param type the class containing the method we want to call.
599: * @param name the name of the method we want to call
600: * @param args the arguments to the call
601: * @return an ApplyExp representing the call
602: */
603: public static synchronized ApplyExp makeInvokeStatic(
604: ClassType type, String name, Expression[] args) {
605: PrimProcedure method = getStaticMethod(type, name, args);
606: if (method == null)
607: throw new RuntimeException("missing or ambiguous method `"
608: + name + "' in " + type.getName());
609: return new ApplyExp(method, args);
610: }
611:
612: public static synchronized PrimProcedure getStaticMethod(
613: ClassType type, String name, Expression[] args) {
614: PrimProcedure[] methods = invokeStatic.getMethods(type, name,
615: null);
616: long num = selectApplicable(methods, type, args, args.length,
617: 0, -1);
618: int okCount = (int) (num >> 32);
619: int maybeCount = (int) num;
620: int index;
621: if (methods == null)
622: index = -1;
623: else if (okCount > 0)
624: index = MethodProc.mostSpecific(methods, okCount);
625: else if (maybeCount == 1)
626: index = 0;
627: else
628: index = -1;
629: return index < 0 ? null : methods[index];
630: }
631: }
|