KickJava   Java API By Example, From Geeks To Geeks.

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


1 package gnu.kawa.reflect;
2 import gnu.mapping.*;
3 import gnu.expr.*;
4 import gnu.bytecode.*;
5 import gnu.lists.FString;
6 import gnu.kawa.lispexpr.LangPrimType;
7
8 public class SlotGet extends Procedure2
9   implements HasSetter, CanInline, Inlineable
10 {
11   static Class JavaDoc[] noClasses = { };
12
13   /** True if this is a "static-field" operation. */
14   boolean isStatic;
15
16   Procedure setter;
17   public static final SlotGet field
18     = new SlotGet("field", false, SlotSet.set$Mnfield$Ex);
19   public static final SlotGet slotRef
20     = new SlotGet("slot-ref", false, SlotSet.set$Mnfield$Ex);
21   public static final SlotGet staticField
22     = new SlotGet("static-field", true, SlotSet.set$Mnstatic$Mnfield$Ex);
23
24   public SlotGet(String JavaDoc name, boolean isStatic)
25   {
26     super(name);
27     this.isStatic = isStatic;
28   }
29
30   public SlotGet(String JavaDoc name, boolean isStatic, Procedure setter)
31   {
32     super(name);
33     this.isStatic = isStatic;
34     this.setter = setter;
35   }
36
37   public static Object JavaDoc field(Object JavaDoc obj, String JavaDoc fname)
38   {
39     return field.apply2(obj, fname);
40   }
41
42   public static Object JavaDoc staticField(Object JavaDoc obj, String JavaDoc fname)
43   {
44     return staticField.apply2(obj, fname);
45   }
46
47   public Object JavaDoc apply2 (Object JavaDoc arg1, Object JavaDoc arg2)
48   {
49     String JavaDoc name, fname;
50     String JavaDoc getName = null, isName = null;
51     if (arg2 instanceof gnu.bytecode.Field)
52       {
53         fname = ((gnu.bytecode.Field) arg2).getName();
54         name = Compilation.demangleName(fname, true);
55       }
56     else if (arg2 instanceof gnu.bytecode.Method)
57       {
58         String JavaDoc mname = ((gnu.bytecode.Method) arg2).getName();
59         name = Compilation.demangleName(mname, false);
60         if (mname.startsWith("get"))
61           getName = mname;
62         else if (mname.startsWith("is"))
63           isName = mname;
64         fname = null;
65       }
66     else if (! (arg2 instanceof String JavaDoc) && ! (arg2 instanceof FString))
67       throw new WrongType(this, 2, arg2, "string");
68     else
69       {
70         name = arg2.toString();
71         fname = gnu.expr.Compilation.mangleNameIfNeeded(name);
72       }
73     // "intern" fname if it is "class" or "length":
74
if ("class".equals(fname))
75       fname = "class";
76     else if ("length".equals(fname))
77       fname = "length";
78     return getSlotValue(isStatic, arg1, name, fname, getName, isName,
79                          Language.getDefaultLanguage());
80   }
81
82   /** The actual gets of finding the field value.
83    * The compiler emits calls to this method if the field name is literals
84    * but the actual field is not known at compile time.
85    * This speeds lookup a bit.
86    */

87   public static Object JavaDoc
88   getSlotValue (boolean isStatic, Object JavaDoc obj, String JavaDoc name, String JavaDoc fname,
89                 String JavaDoc getName, String JavaDoc isName, Language language)
90   {
91     Class JavaDoc clas = isStatic ? coerceToClass(obj) : obj.getClass();
92     if (fname == "length" && clas.isArray())
93       {
94     int length = java.lang.reflect.Array.getLength(obj);
95     return language.coerceToObject(length);
96       }
97     if (fname == "class")
98       return clas;
99     boolean illegalAccess = false;
100     if (fname != null)
101       {
102         java.lang.reflect.Field JavaDoc field;
103         try
104           {
105             field = clas.getField(fname);
106           }
107         catch (Exception JavaDoc ex)
108           {
109             field = null;
110           }
111         if (field != null)
112           {
113             if (isStatic
114                 && (field.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0)
115               throw new RuntimeException JavaDoc("cannot access non-static field '"
116                                          + fname + '\'');
117             try
118               {
119                 return language.coerceToObject(field.getType(), field.get(obj));
120               }
121             catch (IllegalAccessException JavaDoc ex)
122               {
123                 illegalAccess = true;
124               }
125             catch (Exception JavaDoc ex)
126               {
127                 ex.printStackTrace(); // FIXME?
128
}
129           }
130       }
131
132     // Try looking for a method "getFname" or "isFname" instead:
133
try
134       {
135         String JavaDoc mname = null;
136         java.lang.reflect.Method JavaDoc getmethod = null;
137         
138         try {
139           mname = getName != null ? getName
140             : ClassExp.slotToMethodName("get", name);
141           getmethod = clas.getMethod(mname, noClasses);
142         } catch (Exception JavaDoc getEx) {
143           mname = isName != null ? isName
144             : ClassExp.slotToMethodName("is", name);
145           getmethod = clas.getMethod(mname, noClasses);
146         }
147
148         if (isStatic
149             && (getmethod.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0)
150           throw new RuntimeException JavaDoc("cannot call non-static getter method '"
151                                      + mname + '\'');
152         Object JavaDoc result = getmethod.invoke(obj, Values.noArgs);
153         result = language.coerceToObject(getmethod.getReturnType(), result);
154         return result;
155       }
156     catch (java.lang.reflect.InvocationTargetException JavaDoc ex)
157       {
158         throw WrappedException.wrapIfNeeded(ex.getTargetException());
159       }
160     catch (IllegalAccessException JavaDoc ex)
161       {
162         illegalAccess = true;
163       }
164     catch (java.lang.NoSuchMethodException JavaDoc ex)
165       {
166       }
167     if (illegalAccess)
168       throw new RuntimeException JavaDoc("illegal access for field "+fname);
169     else
170       throw new RuntimeException JavaDoc ("no such field "+fname
171                                   +" in "+clas.getName());
172   }
173
174   static Class JavaDoc coerceToClass(Object JavaDoc obj)
175   {
176     if (obj instanceof Class JavaDoc)
177       return (Class JavaDoc) obj;
178     if (obj instanceof gnu.bytecode.Type)
179       return ((gnu.bytecode.Type) obj).getReflectClass();
180     throw new RuntimeException JavaDoc("argument is neither Class nor Type");
181   }
182
183   public void setN (Object JavaDoc[] args)
184   {
185     int nargs = args.length;
186     if (nargs != 3)
187       throw new WrongArguments(getSetter(), nargs);
188     set2(args[0], args[1], args[2]);
189   }
190
191   public void set2 (Object JavaDoc obj, Object JavaDoc name, Object JavaDoc value)
192   {
193     SlotSet.apply(isStatic, obj, (String JavaDoc) name, value);
194   }
195
196   /** Get a named property - field or 'get' accessor method.
197    * @param clas the class type declaring the property.
198    * @param name the source (unmangled) name of the property.
199    */

200   public static Member
201   lookupMember (ClassType clas, String JavaDoc name, ClassType caller)
202   {
203     gnu.bytecode.Field field
204       = clas.getField(Compilation.mangleNameIfNeeded(name), -1);
205     if (field != null)
206       {
207         if (caller == null)
208           caller = Type.pointer_type;
209         if (caller.isAccessible(field.getDeclaringClass(),
210                                 field.getModifiers()))
211           return field;
212       }
213
214     // Try looking for a method "getFname" instead:
215
String JavaDoc getname = ClassExp.slotToMethodName("get", name);
216     gnu.bytecode.Method method = clas.getMethod(getname, Type.typeArray0);
217     if (method == null)
218       return field;
219     else
220       return method;
221   }
222
223   public Expression inline (ApplyExp exp, ExpWalker walker)
224   {
225     Compilation comp = walker.getCompilation();
226     Language language = comp.getLanguage();
227     Type type;
228     Expression[] args = exp.getArgs();
229     Expression arg0 = args[0];
230     Expression arg1 = args[1];
231     String JavaDoc name = null;
232     if (arg1 instanceof QuoteExp)
233       {
234         Object JavaDoc val1 = ((QuoteExp) arg1).getValue();
235         if (val1 instanceof String JavaDoc
236             || val1 instanceof FString
237             || val1 instanceof Symbol)
238           name = val1.toString();
239       }
240     if (isStatic)
241       {
242         type = language.getTypeFor(arg0);
243         int known = Invoke.checkKnownClass(type, comp);
244         if (known < 0)
245           return exp;
246         if ("class".equals(name))
247           {
248             if (known > 0)
249               return QuoteExp.getInstance(type.getReflectClass());
250             Method method
251               = Compilation.typeType.getDeclaredMethod("getReflectClass", 0);
252             return new ApplyExp(method, new Expression[] { arg0 });
253           }
254         if (type != null)
255           {
256             Expression[] nargs
257               = new Expression[] { new QuoteExp(type), arg1 };
258             ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs);
259             nexp.setLine(exp);
260             exp = nexp;
261           }
262       }
263     else
264       type = arg0.getType();
265     if (type instanceof ClassType && name != null)
266       {
267     ClassType ctype = (ClassType) type;
268     ClassType caller = comp.curClass != null ? comp.curClass
269       : comp.mainClass;
270         Member part = lookupMember(ctype, name, caller);
271         if (part instanceof gnu.bytecode.Field)
272           {
273             gnu.bytecode.Field field = (gnu.bytecode.Field) part;
274             ctype = field.getDeclaringClass();
275             int modifiers = field.getModifiers();
276             boolean isStaticField = (modifiers & Access.STATIC) != 0;
277             if (isStatic && ! isStaticField)
278               return new ErrorExp("cannot access non-static field `" + name
279                                   + "' using `" + getName() + '\'', comp);
280         if (caller != null && ! caller.isAccessible(ctype, modifiers))
281           return new ErrorExp("field "+ctype.getName()+'.'+name
282                                   +" is not accessible here", comp);
283           }
284
285         else if (part instanceof gnu.bytecode.Method)
286           {
287             gnu.bytecode.Method method = (gnu.bytecode.Method) part;
288             ctype = method.getDeclaringClass();
289         int modifiers = method.getModifiers();
290             boolean isStaticMethod = method.getStaticFlag();
291             if (isStatic && ! isStaticMethod)
292               return new ErrorExp("cannot call non-static getter method `"
293                                   + name + "' using `" + getName() + '\'', comp);
294         if (caller != null && ! caller.isAccessible(ctype, modifiers))
295           return new ErrorExp( "method "+method +" is not accessible here",
296                                    comp);
297           }
298         if (part != null)
299           {
300             Expression[] nargs
301               = new Expression[] { arg0, new QuoteExp(part) };
302             ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs);
303             nexp.setLine(exp);
304             return nexp;
305           }
306         if (type != Type.pointer_type)
307           comp.error('e', "no slot `"+name+"' in "+ctype.getName());
308       }
309     if (name != null && ! (type instanceof ArrayType))
310       {
311         String JavaDoc fname = gnu.expr.Compilation.mangleNameIfNeeded(name);
312         // So we can quickly check for "class" or "length".
313
// The name gets interned anyway when compiled.
314
fname = fname.intern();
315         String JavaDoc getName = ClassExp.slotToMethodName("get", name);
316         String JavaDoc isName = ClassExp.slotToMethodName("is", name);
317         ApplyExp nexp
318           = new ApplyExp(Invoke.invokeStatic,
319                          new Expression[] {
320                            QuoteExp.getInstance("gnu.kawa.reflect.SlotGet"),
321                            QuoteExp.getInstance("getSlotValue"),
322                            isStatic ? QuoteExp.trueExp : QuoteExp.falseExp,
323                            args[0],
324                            QuoteExp.getInstance(name),
325                            QuoteExp.getInstance(fname),
326                            QuoteExp.getInstance(getName),
327                            QuoteExp.getInstance(isName),
328                            QuoteExp.getInstance(language)});
329         nexp.setLine(exp);
330         return ((InlineCalls) walker).walkApplyOnly(nexp);
331       }
332     return exp;
333   }
334
335   public void compile (ApplyExp exp, Compilation comp, Target target)
336   {
337     Expression[] args = exp.getArgs();
338     Expression arg0 = args[0];
339     Expression arg1 = args[1];
340     Language language = comp.getLanguage();
341     Type type = isStatic ? language.getTypeFor(arg0)
342       : arg0.getType();
343     CodeAttr code = comp.getCode();
344     if (type instanceof ClassType && arg1 instanceof QuoteExp)
345       {
346     ClassType ctype = (ClassType) type;
347         Object JavaDoc part = ((QuoteExp) arg1).getValue();
348         if (part instanceof gnu.bytecode.Field)
349           {
350             gnu.bytecode.Field field = (gnu.bytecode.Field) part;
351             int modifiers = field.getModifiers();
352             boolean isStaticField = (modifiers & Access.STATIC) != 0;
353             args[0].compile(comp,
354                             isStaticField ? Target.Ignore
355                             : Target.pushValue(ctype));
356             if (isStaticField)
357               {
358                 boolean inlined = false;
359                 /*
360                 FIXME This isn't quite safe. We should only "inline"
361                 the value if the field whose initializer is a constant
362                 expression (JLS 2nd ed 15.28). We cannot determine this
363                 using reflection instead we have to parse the .class file.
364
365                 Type ftype = field.getType();
366                 if ((modifiers & Access.FINAL) != 0
367                     && ftype instanceof PrimType)
368                   {
369                     // We inline int final fields.
370                     // Other kinds of final fields are less obviously a win.
371                     char sig = ftype.getSignature().charAt(0);
372                     if (sig != 'F' && sig != 'D' && sig != 'J')
373                       {
374                         try
375                           {
376                             java.lang.reflect.Field rfield
377                               = field.getReflectField();
378                             int val = rfield.getInt(null);
379                             code.emitPushInt(val);
380                             inlined = true;
381                           }
382                         catch (Exception ex)
383                           {
384                           }
385                       }
386                   }
387                 */

388                 if (! inlined)
389                   code.emitGetStatic(field);
390               }
391             else
392               code.emitGetField(field);
393         Type ftype = field.getType();
394         Class JavaDoc fclass = ftype.getReflectClass();
395         if (fclass != null)
396           ftype = language.getTypeFor(fclass);
397         target.compileFromStack(comp, ftype);
398             return;
399           }
400         if (part instanceof Method)
401           {
402             gnu.bytecode.Method method = (gnu.bytecode.Method) part;
403         int modifiers = method.getModifiers();
404             boolean isStaticMethod = method.getStaticFlag();
405             args[0].compile(comp,
406                             isStaticMethod ? Target.Ignore
407                             : Target.pushValue(ctype));
408             if (isStaticMethod)
409               code.emitInvokeStatic(method);
410             else if (ctype.isInterface())
411               code.emitInvokeInterface(method);
412             else
413               code.emitInvokeVirtual(method);
414         target.compileFromStack(comp, method.getReturnType());
415             return;
416           }
417       }
418     String JavaDoc name = ClassMethods.checkName(arg1);
419     if (type instanceof ArrayType && "length".equals(name) && ! isStatic)
420       {
421     args[0].compile(comp, Target.pushValue(type));
422     code.emitArrayLength();
423     target.compileFromStack(comp, LangPrimType.intType); // FIXME
424
return;
425       }
426     ApplyExp.compile(exp, comp, target);
427   }
428
429   public Type getReturnType (Expression[] args)
430   {
431     int nargs = args.length;
432     if (nargs == 2)
433       {
434         Expression arg0 = args[0];
435         Expression arg1 = args[1];
436         if (arg1 instanceof QuoteExp)
437           {
438             Object JavaDoc part = ((QuoteExp) arg1).getValue();
439             if (part instanceof gnu.bytecode.Field)
440               return ((gnu.bytecode.Field) part).getType();
441             if (part instanceof gnu.bytecode.Method)
442               return ((gnu.bytecode.Method) part).getReturnType();
443             if (! isStatic && arg0.getType() instanceof ArrayType
444                 && "length".equals(ClassMethods.checkName(arg1, true)))
445               return gnu.kawa.lispexpr.LangPrimType.intType; // FIXME
446
}
447       }
448     return Type.pointer_type;
449   }
450
451   public Procedure getSetter()
452   {
453     return setter == null ? super.getSetter() : setter;
454   }
455
456   /**
457    * Convenience method to make an Expression that gets the value of a field.
458    * @param value evaluates to object that has the named field
459    * @param fieldName name of field in value
460    * @return expression that get the name field from value
461    */

462   public static ApplyExp makeGetField(Expression value, String JavaDoc fieldName)
463   {
464     Expression[] args = new Expression[2];
465     args[0] = value;
466     args[1] = new QuoteExp(fieldName);
467     return new ApplyExp(gnu.kawa.reflect.SlotGet.field, args);
468   }
469 }
470
Popular Tags