KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > kawa > reflect > Invoke


1 package gnu.kawa.reflect;
2 import gnu.mapping.*;
3 import gnu.expr.*;
4 import gnu.bytecode.*;
5 import gnu.lists.FString;
6 import java.lang.reflect.Array JavaDoc;
7 import gnu.kawa.lispexpr.ClassNamespace; // FIXME
8

9 public class Invoke extends ProcedureN implements CanInline
10 {
11   /** The kind on invoke operation.
12    * 'N' - make (new).
13    * 'S' - invoke-static (static or non-static):
14    * The first operand is a Class or Type, the second is the name,
15    * and if the is non-static the 3rd is the receiver.
16    * 's' - Like 'S' but only allow static methods. [not used]
17    * 'V' - non-static invoke, only allow non-static methods. [not used]
18    * '*' - non-static invoke, can match static methods also.
19    * This is Java's 'Primary.MethodName(args)' - if the selected method
20    * is static, we only use Primary's type for method select,
21    * but ignore its value.
22    */

23   char kind;
24
25   Language language;
26
27   public static final Invoke invoke = new Invoke("invoke", '*');
28   public static final Invoke invokeStatic = new Invoke("invoke-static", 'S');
29   public static final Invoke invokeSpecial = new Invoke("invoke-special", 'P');
30   public static final Invoke make = new Invoke("make", 'N');
31
32   public Invoke(String JavaDoc name, char kind)
33   {
34     super(name);
35     this.kind = kind;
36     this.language = Language.getDefaultLanguage();
37   }
38
39   public Invoke(String JavaDoc name, char kind, Language language)
40   {
41     super(name);
42     this.kind = kind;
43     this.language = language;
44   }
45
46   public static Object JavaDoc invoke$V(Object JavaDoc[] args) throws Throwable JavaDoc
47   {
48     return invoke.applyN(args);
49   }
50
51   public static Object JavaDoc invokeStatic$V(Object JavaDoc[] args) throws Throwable JavaDoc
52   {
53     return invokeStatic.applyN(args);
54   }
55
56   public static Object JavaDoc make$V(Object JavaDoc[] args) throws Throwable JavaDoc
57   {
58     return make.applyN(args);
59   }
60
61   private static ObjectType typeFrom (Object JavaDoc arg, Invoke thisProc)
62   {
63     if (arg instanceof Class JavaDoc)
64       arg = Type.make((Class JavaDoc) arg);
65     if (arg instanceof ObjectType)
66       return (ObjectType) arg;
67     if (arg instanceof String JavaDoc || arg instanceof FString)
68       return ClassType.make(arg.toString());
69     if (arg instanceof Symbol)
70       return ClassType.make(((Symbol) arg).getName());
71     if (arg instanceof ClassNamespace)
72       return ((ClassNamespace) arg).getClassType();
73     throw new WrongType(thisProc, 0, arg, "class-specifier");
74   }
75
76   public void apply (CallContext ctx) throws Throwable JavaDoc
77   {
78     Object JavaDoc[] args = ctx.getArgs();
79     if (kind=='S' || kind=='V' || kind=='s' || kind=='*')
80       {
81         // The following is an optimization, so that output from the
82
// method is sent directly to ctx.consumer, rather than reified.
83
int nargs = args.length;
84         Procedure.checkArgCount(this, nargs);
85         Object JavaDoc arg0 = args[0];
86         ClassType dtype = (ClassType)
87           ((kind == 'S' || kind == 's') ? typeFrom(arg0, this)
88            : Type.make(arg0.getClass()));
89         Procedure proc = lookupMethods(dtype, args[1]);
90         Object JavaDoc[] margs = new Object JavaDoc[nargs-(kind == 'S' ? 2 : 1)];
91         int i = 0;
92         if (kind == 'V' || kind == '*')
93           margs[i++] = args[0];
94         System.arraycopy(args, 2, margs, i, nargs - 2);
95         proc.checkN(margs, ctx);
96       }
97     else
98       ctx.writeValue(this.applyN(args));
99   }
100
101   public Object JavaDoc applyN (Object JavaDoc[] args) throws Throwable JavaDoc
102   {
103     if (kind == 'P')
104       throw new RuntimeException JavaDoc(getName()
105                                  + ": invoke-special not allowed at run time");
106     
107     int nargs = args.length;
108     Procedure.checkArgCount(this, nargs);
109     Object JavaDoc arg0 = args[0];
110     ObjectType dtype = (kind != 'V' && kind != '*' ? typeFrom(arg0, this)
111                        : (ObjectType) Type.make(arg0.getClass()));
112     Object JavaDoc mname;
113     if (kind == 'N')
114       {
115     mname = null;
116         if (dtype instanceof TypeValue)
117           {
118             Procedure constructor = ((TypeValue) dtype).getConstructor();
119             if (constructor != null)
120               {
121                 nargs--;
122                 Object JavaDoc[] xargs = new Object JavaDoc[nargs];
123                 System.arraycopy(args, 1, xargs, 0, nargs);
124                 return constructor.applyN(xargs);
125               }
126           }
127     if (dtype instanceof PairClassType)
128       {
129         PairClassType ptype = (PairClassType) dtype;
130         dtype = ptype.instanceType;
131       }
132         if (dtype instanceof ArrayType)
133           {
134             Type elementType = ((ArrayType) dtype).getComponentType();
135             int len;
136             len = args.length-1;
137             String JavaDoc name;
138             int length;
139             int i;
140             boolean lengthSpecified;
141             if (len >= 2 && args[1] instanceof Keyword
142                 && ("length".equals(name = ((Keyword) args[1]).getName())
143                     || "size".equals(name)))
144               {
145                 length = ((Number JavaDoc) args[2]).intValue();
146                 i = 3;
147                 lengthSpecified = true;
148               }
149             else
150               {
151                 length = len;
152                 i = 1;
153                 lengthSpecified = false;
154               }
155             Object JavaDoc arr = Array.newInstance(elementType.getReflectClass(),
156                                            length);
157             int index = 0;
158             for (; i <= len; i++)
159               {
160                 Object JavaDoc arg = args[i];
161                 if (lengthSpecified && arg instanceof Keyword && i < len)
162                   {
163                     String JavaDoc kname = ((Keyword) arg).getName();
164                     try
165                       {
166                         index = Integer.parseInt(kname);
167                       }
168                     catch (Throwable JavaDoc ex)
169                       {
170                         throw new RuntimeException JavaDoc("non-integer keyword '"+kname+"' in array constructor");
171                       }
172                     arg = args[++i];
173                   }
174                 Array.set(arr, index, elementType.coerceFromObject(arg));
175                 index++;
176               }
177             return arr;
178           }
179       }
180     else
181       {
182         mname = args[1];
183       }
184     MethodProc proc = lookupMethods((ClassType) dtype, mname);
185     if (kind != 'N')
186       {
187         Object JavaDoc[] margs = new Object JavaDoc[nargs-(kind == 'S' || kind == 's' ? 2 : 1)];
188         int i = 0;
189         if (kind == 'V' || kind == '*')
190           margs[i++] = args[0];
191         System.arraycopy(args, 2, margs, i, nargs - 2);
192         return proc.applyN(margs);
193       }
194     else
195       {
196         CallContext vars = CallContext.getInstance();
197         int err = proc.matchN(args, vars);
198         if (err == 0)
199           return vars.runUntilValue();
200         else if ((nargs & 1) == 1)
201           {
202             // Check if args is a set of (keyword,value)-pairs.
203
for (int i = 1; i < nargs; i += 2)
204               {
205                 if (! (args[i] instanceof Keyword))
206                   throw MethodProc.matchFailAsException(err, proc, args);
207               }
208
209             Object JavaDoc result;
210             result = proc.apply1(args[0]);
211             for (int i = 1; i < nargs; i += 2)
212               {
213                 Keyword key = (Keyword) args[i];
214                 Object JavaDoc arg = args[i+1];
215                 SlotSet.apply(false, result, key.getName(), arg);
216               }
217             return result;
218           }
219         throw MethodProc.matchFailAsException(err, proc, args);
220       }
221   }
222
223   public int numArgs()
224   {
225     return (-1 << 12) | (kind == 'N' ? 1 : 2);
226   }
227
228   private PrimProcedure[] cacheMethods;
229   private Expression[] cacheArgs;
230   private int cacheDefinitelyApplicableMethodCount;
231   private int cachePossiblyApplicableMethodCount;
232
233   protected MethodProc lookupMethods(ClassType dtype, Object JavaDoc name)
234   {
235     String JavaDoc mname;
236     if (kind == 'N')
237       mname = "<init>";
238     else
239       {
240         if (name instanceof String JavaDoc || name instanceof FString)
241           mname = name.toString();
242     else if (name instanceof Symbol)
243       mname = ((Symbol) name).getName();
244         else
245           throw new WrongType(this, 1, null);
246         mname = Compilation.mangleName(mname);
247       }
248     MethodProc proc = ClassMethods.apply(dtype, mname,
249                                          kind == 'P' ? 'P'
250                                          : kind == '*' || kind == 'V' ? 'V'
251                                          : '\0',
252                                          language);
253     if (proc == null)
254       throw new RuntimeException JavaDoc(getName() + ": no method named `"
255                                  + mname + "' in class " + dtype.getName());
256     return proc;
257   }
258
259   protected PrimProcedure[] getMethods(ClassType ctype, String JavaDoc mname,
260                                        Expression[] args, int margsLength,
261                                        int argsStartIndex, int objIndex,
262                        ClassType caller)
263   {
264     if (args == cacheArgs)
265       return cacheMethods;
266
267     Type[] atypes = new Type[margsLength];
268
269     int dst = 0;
270     if (objIndex >= 0)
271       atypes[dst++] = ctype;
272     for (int src = argsStartIndex;
273          src < args.length && dst < atypes.length;
274          src++, dst++)
275       atypes[dst] = args[src].getType();
276
277     PrimProcedure[] methods
278       = ClassMethods.getMethods(ctype, mname,
279                                 kind == 'P' ? 'P'
280                                 : kind == '*' || kind == 'V' ? 'V'
281                                 : '\0',
282                                 caller, language);
283     
284     long num = ClassMethods.selectApplicable(methods, atypes);
285     cacheArgs = args;
286     cacheDefinitelyApplicableMethodCount = (int) (num >> 32);
287     cachePossiblyApplicableMethodCount = (int) num;
288     cacheMethods = methods;
289     return cacheMethods;
290   }
291
292   /** Return an array if args (starting with start) is a set of
293    * (keyword, value)-value pairs. */

294   static Object JavaDoc[] checkKeywords(Type type, Expression[] args,
295                                 int start, ClassType caller)
296   {
297     int len = args.length;
298     if (((len - start) & 1) != 0)
299       return null;
300     Object JavaDoc[] fields = new Object JavaDoc[(len-start) >> 1];
301     for (int i = fields.length; -- i>= 0; )
302       {
303         Expression arg = args[start + 2 * i];
304         if (! (arg instanceof QuoteExp))
305           return null;
306         Object JavaDoc value = ((QuoteExp) arg).getValue();
307         if (! (value instanceof Keyword))
308           return null;
309         String JavaDoc name = ((Keyword) value).getName();
310         Member slot = SlotSet.lookupMember((ClassType) type, name, caller);
311         fields[i] = slot != null ? (Object JavaDoc) slot : (Object JavaDoc) name;
312       }
313     return fields;
314   }
315
316   /** Check if class exists.
317    * @return 1 if class actually exists;
318    * -1 is class should exist, but doesn't;
319    * and 0 otherwise.
320    */

321   public static int checkKnownClass (Type type, Compilation comp)
322   {
323     if (type instanceof ClassType && ((ClassType) type).isExisting())
324       {
325         try
326           {
327             type.getReflectClass();
328             return 1;
329           }
330         catch (Exception JavaDoc ex)
331           {
332             comp.error('e', "unknown class: " + type.getName());
333             return -1;
334           }
335       }
336     return 0;
337   }
338
339   /** Resolve class specifier to ClassType at inline time.
340    * This is an optimization to avoid having a module-level binding
341    * created for the class name. */

342
343   public static ApplyExp inlineClassName (ApplyExp exp, int carg,
344                                           InlineCalls walker)
345   {
346     Compilation comp = walker.getCompilation();
347     Language language = comp.getLanguage();
348     Expression[] args = exp.getArgs();
349     if (args.length > carg)
350       {
351     Type type = language.getTypeFor(args[carg]);
352     if (! (type instanceof Type))
353       return exp;
354         checkKnownClass(type, comp);
355     Expression[] nargs = new Expression[args.length];
356     System.arraycopy(args, 0, nargs, 0, args.length);
357     nargs[carg] = new QuoteExp(type);
358     ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs);
359         nexp.setLine(exp);
360         return nexp;
361       }
362     return exp;
363   }
364
365   public Expression inline (ApplyExp exp, ExpWalker walker)
366   {
367     Compilation comp = walker.getCompilation();
368     Expression[] args = exp.getArgs();
369     int nargs = args.length;
370     if (! comp.mustCompile
371         // This should never happen, as InlineCalls.walkApplyExp
372
// checks the number of arguments before inline is called.
373
|| nargs == 0 || ((kind == 'V' || kind == '*') && nargs == 1))
374       return exp;
375     ObjectType type;
376     Expression arg0 = args[0];
377     Type type0 = (kind == 'V' || kind == '*' ? arg0.getType() : language.getTypeFor(arg0));
378     if (type0 instanceof PairClassType)
379       type = ((PairClassType) type0).instanceType;
380     else if (type0 instanceof ObjectType)
381       type = (ObjectType) type0;
382     else
383       type = null;
384     String JavaDoc name = getMethodName(args);
385
386     int margsLength, argsStartIndex, objIndex;
387     if (kind == 'V' || kind == '*') // Invoke virtual
388
{
389         margsLength = nargs - 1;
390         argsStartIndex = 2;
391         objIndex = 0;
392       }
393     else if (kind == 'N') // make new
394
{
395         margsLength = nargs;
396         argsStartIndex = 0;
397         objIndex = -1;
398       }
399     else if (kind == 'S' || kind == 's') // Invoke static
400
{
401         margsLength = nargs - 2;
402         argsStartIndex = 2;
403         objIndex = -1;
404       }
405     else if (kind == 'P') // Invoke special
406
{
407         margsLength = nargs - 2;
408         argsStartIndex = 3;
409         objIndex = 1;
410       }
411     else
412       return exp;
413
414     if (kind == 'N' && type instanceof ArrayType)
415       {
416         ArrayType atype = (ArrayType) type;
417         Type elementType = atype.getComponentType();
418         Expression sizeArg = null;
419         boolean lengthSpecified = false;
420         if (args.length >= 3 && args[1] instanceof QuoteExp)
421           {
422             Object JavaDoc arg1 = ((QuoteExp) args[1]).getValue();
423             if (arg1 instanceof Keyword
424                  && ("length".equals(name = ((Keyword) arg1).getName())
425                      || "size".equals(name)))
426               {
427                 sizeArg = args[2];
428                 lengthSpecified = true;
429               }
430           }
431         if (sizeArg == null)
432           sizeArg = QuoteExp.getInstance(new Integer JavaDoc(args.length-1));
433         Expression alloc = new ApplyExp(new ArrayNew(elementType),
434                                         new Expression[] { sizeArg } );
435         if (lengthSpecified && args.length == 3)
436           return alloc;
437         LetExp let = new LetExp(new Expression[] { alloc });
438         Declaration adecl = let.addDeclaration((String JavaDoc) null, atype);
439         adecl.noteValue(alloc);
440         BeginExp begin = new BeginExp();
441         int index = 0;
442         for (int i = lengthSpecified ? 3 : 1; i < args.length; i++)
443           {
444             Expression arg = args[i];
445             if (lengthSpecified && i+1 < args.length && arg instanceof QuoteExp)
446               {
447                 Object JavaDoc key = ((QuoteExp) arg).getValue();
448                 if (key instanceof Keyword)
449                   {
450                     String JavaDoc kname = ((Keyword) key).getName();
451                     try
452                       {
453                         index = Integer.parseInt(kname);
454                         arg = args[++i];
455                       }
456                     catch (Throwable JavaDoc ex)
457                       {
458                         comp.error('e', "non-integer keyword '"+kname+"' in array constructor");
459                         return exp;
460                       }
461                   }
462               }
463             begin.add(new ApplyExp(new ArraySet(elementType),
464                                    new Expression[] {
465                                      new ReferenceExp(adecl),
466                                      QuoteExp.getInstance(new Integer JavaDoc(index)),
467                                      arg}));
468             index++;
469           }
470         begin.add(new ReferenceExp(adecl));
471         let.body = begin;
472         return let;
473       }
474     else if (type != null && name != null)
475       {
476         if (type instanceof TypeValue && kind == 'N')
477           {
478             Procedure constructor = ((TypeValue) type).getConstructor();
479             if (constructor != null)
480               {
481                 Expression[] xargs = new Expression[nargs];
482                 System.arraycopy(args, 1, xargs, 0, nargs-1);
483                 return ((InlineCalls) walker)
484                   .walkApplyOnly(new ApplyExp(constructor, xargs));
485               }
486           }
487         PrimProcedure[] methods;
488         int okCount, maybeCount;
489         ClassType caller = comp == null ? null
490           : comp.curClass != null ? comp.curClass
491           : comp.mainClass;
492         synchronized (this)
493           {
494             try
495               {
496                 methods = getMethods((ClassType) type, name, args,
497                                      margsLength, argsStartIndex, objIndex,
498                      caller);
499               }
500             catch (Exception JavaDoc ex)
501               {
502                 comp.error('w', "unknown class: " + type.getName());
503                 methods = null;
504               }
505             okCount = cacheDefinitelyApplicableMethodCount;
506             maybeCount = cachePossiblyApplicableMethodCount;
507           }
508         if (methods != null)
509           {
510             int index = -1;
511             if (methods.length == 0)
512           {
513         if (comp.getBooleanOption("warn-invoke-unknown-method", true))
514           comp.error('w', "no accessible method '"+name+"' in "+type.getName());
515           }
516             else if (okCount + maybeCount == 0)
517               {
518                 Object JavaDoc[] slots;
519                 if (kind == 'N'
520                     && (ClassMethods.selectApplicable(methods,
521                                                       new Type[] { Compilation.typeClassType })
522                         >> 32) == 1
523                     && (slots = checkKeywords(type, args, 1, caller)) != null)
524                   {
525                     StringBuffer JavaDoc errbuf = null;
526                     for (int i = 0; i < slots.length; i++)
527                       {
528                         if (slots[i] instanceof String JavaDoc)
529                           {
530                             if (errbuf == null)
531                               {
532                                 errbuf = new StringBuffer JavaDoc();
533                                 errbuf.append("no field or setter ");
534                               }
535                             else
536                               errbuf.append(", ");
537                             errbuf.append('`');
538                             errbuf.append(slots[i]);
539                             errbuf.append('\'');
540                           }
541                       }
542                     if (errbuf != null)
543                       {
544                         errbuf.append(" in class ");
545                         errbuf.append(type.getName());
546                         comp.error('w', errbuf.toString());
547                       }
548                     else
549                       {
550             ApplyExp e = new ApplyExp(methods[0],
551                                                   new Expression[] { arg0 });
552                         for (int i = 0; i < slots.length; i++)
553                           {
554                 Expression[] sargs
555                   = { e, new QuoteExp(slots[i]), args[2 * i + 2] };
556                 e = new ApplyExp(SlotSet.setFieldReturnObject,
557                          sargs);
558                          }
559             return e.setLine(exp);
560                       }
561                   }
562                 else
563                   comp.error('w', "no possibly applicable method '"
564                              +name+"' in "+type.getName());
565               }
566             else if (okCount == 1 || (okCount == 0 && maybeCount == 1))
567               index = 0;
568             else if (okCount > 0)
569               {
570                 index = MethodProc.mostSpecific(methods, okCount);
571                 if (index < 0)
572           {
573             if (kind == 'S')
574               {
575             // If we didn't find a most specific method,
576
// check if there is one that is static. If so,
577
// prefer that - after all, we're using invoke-static.
578
for (int i = 0; i < okCount; i++)
579               {
580                 if (methods[i].getStaticFlag())
581                   {
582                 if (index >= 0)
583                   {
584                     index = -1;
585                     break;
586                   }
587                 else
588                   index = i;
589                   }
590               }
591               }
592           }
593                 if (index < 0
594             && comp.getBooleanOption("warn-invoke-unknown-method",
595                          true))
596           {
597                     StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
598                     sbuf.append("more than one definitely applicable method `");
599                     sbuf.append(name);
600                     sbuf.append("' in ");
601                     sbuf.append(type.getName());
602                     append(methods, okCount, sbuf);
603             comp.error('w', sbuf.toString());
604           }
605               }
606         else if (comp.getBooleanOption("warn-invoke-unknown-method", true))
607               {
608                 StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
609                 sbuf.append("more than one possibly applicable method '");
610                 sbuf.append(name);
611                 sbuf.append("' in ");
612                 sbuf.append(type.getName());
613                 append(methods, maybeCount, sbuf);
614                 comp.error('w', sbuf.toString());
615           }
616             if (index >= 0)
617               {
618                 Expression[] margs = new Expression[margsLength];
619                 int dst = 0;
620                 if (objIndex >= 0)
621                   margs[dst++] = args[objIndex];
622                 for (int src = argsStartIndex;
623                      src < args.length && dst < margs.length;
624                      src++, dst++)
625                   margs[dst] = args[src];
626                 return new ApplyExp(methods[index], margs).setLine(exp);
627               }
628           }
629       }
630     return exp;
631   }
632
633   private void append (PrimProcedure[] methods, int mcount, StringBuffer JavaDoc sbuf)
634   {
635     for (int i = 0; i < mcount; i++)
636       {
637         sbuf.append("\n candidate: ");
638         sbuf.append(methods[i]);
639       }
640   }
641
642   private String JavaDoc getMethodName(Expression[] args)
643   {
644     if (kind == 'N')
645       return "<init>";
646     int nameIndex = (kind == 'P' ? 2 : 1);
647     if (args.length >= nameIndex + 1)
648       return ClassMethods.checkName(args[nameIndex], false);
649     return null;
650   }
651
652   /** Return an ApplyExp that will call a method with given arguments.
653    * @param type the class containing the method we want to call.
654    * @param name the name of the method we want to call
655    * @param args the arguments to the call
656    * @return an ApplyExp representing the call
657    */

658   public static synchronized
659   ApplyExp makeInvokeStatic(ClassType type, String JavaDoc name, Expression[] args)
660   {
661     PrimProcedure method = getStaticMethod(type, name, args);
662     if (method == null)
663       throw new RuntimeException JavaDoc("missing or ambiguous method `" + name
664                                  + "' in " + type.getName());
665     return new ApplyExp(method, args);
666   }
667
668   public static synchronized PrimProcedure
669   getStaticMethod(ClassType type, String JavaDoc name, Expression[] args)
670   {
671     PrimProcedure[] methods = invokeStatic.getMethods(type, name, args,
672                                                       args.length, 0, -1, null);
673     int okCount = invokeStatic.cacheDefinitelyApplicableMethodCount;
674     int maybeCount = invokeStatic.cachePossiblyApplicableMethodCount;
675     int index;
676     if (methods == null)
677       index = -1;
678     else if (okCount > 0)
679       index = MethodProc.mostSpecific(methods, okCount);
680     else if (maybeCount == 1)
681       index = 0;
682     else
683       index = -1;
684     return index < 0 ? null : methods[index];
685   }
686 }
687
Popular Tags