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 gnu.kawa.lispexpr.LangPrimType;
008:
009: public class SlotGet extends Procedure2 implements HasSetter,
010: CanInline, Inlineable {
011: static Class[] noClasses = {};
012:
013: /** True if this is a "static-field" operation. */
014: boolean isStatic;
015:
016: Procedure setter;
017: public static final SlotGet field = new SlotGet("field", false,
018: SlotSet.set$Mnfield$Ex);
019: public static final SlotGet slotRef = new SlotGet("slot-ref",
020: false, SlotSet.set$Mnfield$Ex);
021: public static final SlotGet staticField = new SlotGet(
022: "static-field", true, SlotSet.set$Mnstatic$Mnfield$Ex);
023:
024: public SlotGet(String name, boolean isStatic) {
025: super (name);
026: this .isStatic = isStatic;
027: }
028:
029: public SlotGet(String name, boolean isStatic, Procedure setter) {
030: super (name);
031: this .isStatic = isStatic;
032: this .setter = setter;
033: }
034:
035: public static Object field(Object obj, String fname) {
036: return field.apply2(obj, fname);
037: }
038:
039: public static Object staticField(Object obj, String fname) {
040: return staticField.apply2(obj, fname);
041: }
042:
043: public Object apply2(Object arg1, Object arg2) {
044: String name, fname;
045: String getName = null, isName = null;
046: if (arg2 instanceof gnu.bytecode.Field) {
047: fname = ((gnu.bytecode.Field) arg2).getName();
048: name = Compilation.demangleName(fname, true);
049: } else if (arg2 instanceof gnu.bytecode.Method) {
050: String mname = ((gnu.bytecode.Method) arg2).getName();
051: name = Compilation.demangleName(mname, false);
052: if (mname.startsWith("get"))
053: getName = mname;
054: else if (mname.startsWith("is"))
055: isName = mname;
056: fname = null;
057: } else if (!(arg2 instanceof String)
058: && !(arg2 instanceof FString))
059: throw new WrongType(this , 2, arg2, "string");
060: else {
061: name = arg2.toString();
062: fname = gnu.expr.Compilation.mangleNameIfNeeded(name);
063: }
064: // "intern" fname if it is "class" or "length":
065: if ("class".equals(fname))
066: fname = "class";
067: else if ("length".equals(fname))
068: fname = "length";
069: return getSlotValue(isStatic, arg1, name, fname, getName,
070: isName, Language.getDefaultLanguage());
071: }
072:
073: /** The actual gets of finding the field value.
074: * The compiler emits calls to this method if the field name is literals
075: * but the actual field is not known at compile time.
076: * This speeds lookup a bit.
077: */
078: public static Object getSlotValue(boolean isStatic, Object obj,
079: String name, String fname, String getName, String isName,
080: Language language) {
081: Class clas = isStatic ? coerceToClass(obj) : obj.getClass();
082: if (fname == "length" && clas.isArray()) {
083: int length = java.lang.reflect.Array.getLength(obj);
084: return language.coerceToObject(length);
085: }
086: if (fname == "class")
087: return clas;
088: boolean illegalAccess = false;
089: if (fname != null) {
090: java.lang.reflect.Field field;
091: try {
092: field = clas.getField(fname);
093: } catch (Exception ex) {
094: field = null;
095: }
096: if (field != null) {
097: if (isStatic
098: && (field.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0)
099: throw new RuntimeException(
100: "cannot access non-static field '" + fname
101: + '\'');
102: try {
103: return language.coerceToObject(field.getType(),
104: field.get(obj));
105: } catch (IllegalAccessException ex) {
106: illegalAccess = true;
107: } catch (Exception ex) {
108: ex.printStackTrace(); // FIXME?
109: }
110: }
111: }
112:
113: // Try looking for a method "getFname" or "isFname" instead:
114: try {
115: String mname = null;
116: java.lang.reflect.Method getmethod = null;
117:
118: try {
119: mname = getName != null ? getName : ClassExp
120: .slotToMethodName("get", name);
121: getmethod = clas.getMethod(mname, noClasses);
122: } catch (Exception getEx) {
123: mname = isName != null ? isName : ClassExp
124: .slotToMethodName("is", name);
125: getmethod = clas.getMethod(mname, noClasses);
126: }
127:
128: if (isStatic
129: && (getmethod.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0)
130: throw new RuntimeException(
131: "cannot call non-static getter method '"
132: + mname + '\'');
133: Object result = getmethod.invoke(obj, Values.noArgs);
134: result = language.coerceToObject(getmethod.getReturnType(),
135: result);
136: return result;
137: } catch (java.lang.reflect.InvocationTargetException ex) {
138: throw WrappedException
139: .wrapIfNeeded(ex.getTargetException());
140: } catch (IllegalAccessException ex) {
141: illegalAccess = true;
142: } catch (java.lang.NoSuchMethodException ex) {
143: }
144: if (illegalAccess)
145: throw new RuntimeException("illegal access for field "
146: + fname);
147: else
148: throw new RuntimeException("no such field " + fname
149: + " in " + clas.getName());
150: }
151:
152: static Class coerceToClass(Object obj) {
153: if (obj instanceof Class)
154: return (Class) obj;
155: if (obj instanceof gnu.bytecode.Type)
156: return ((gnu.bytecode.Type) obj).getReflectClass();
157: throw new RuntimeException("argument is neither Class nor Type");
158: }
159:
160: public void setN(Object[] args) {
161: int nargs = args.length;
162: if (nargs != 3)
163: throw new WrongArguments(getSetter(), nargs);
164: set2(args[0], args[1], args[2]);
165: }
166:
167: public void set2(Object obj, Object name, Object value) {
168: SlotSet.apply(isStatic, obj, (String) name, value);
169: }
170:
171: /** Get a named property - field or 'get' accessor method.
172: * @param clas the class type declaring the property.
173: * @param name the source (unmangled) name of the property.
174: */
175: public static Member lookupMember(ClassType clas, String name,
176: ClassType caller) {
177: gnu.bytecode.Field field = clas.getField(Compilation
178: .mangleNameIfNeeded(name), -1);
179: if (field != null) {
180: if (caller == null)
181: caller = Type.pointer_type;
182: if (caller.isAccessible(field.getDeclaringClass(), field
183: .getModifiers()))
184: return field;
185: }
186:
187: // Try looking for a method "getFname" instead:
188: String getname = ClassExp.slotToMethodName("get", name);
189: gnu.bytecode.Method method = clas.getMethod(getname,
190: Type.typeArray0);
191: if (method == null)
192: return field;
193: else
194: return method;
195: }
196:
197: public Expression inline(ApplyExp exp, ExpWalker walker) {
198: Compilation comp = walker.getCompilation();
199: Language language = comp.getLanguage();
200: Type type;
201: Expression[] args = exp.getArgs();
202: Expression arg0 = args[0];
203: Expression arg1 = args[1];
204: String name = null;
205: if (arg1 instanceof QuoteExp) {
206: Object val1 = ((QuoteExp) arg1).getValue();
207: if (val1 instanceof String || val1 instanceof FString
208: || val1 instanceof Symbol)
209: name = val1.toString();
210: }
211: if (isStatic) {
212: type = language.getTypeFor(arg0);
213: int known = Invoke.checkKnownClass(type, comp);
214: if (known < 0)
215: return exp;
216: if ("class".equals(name)) {
217: if (known > 0)
218: return QuoteExp.getInstance(type.getReflectClass());
219: Method method = Compilation.typeType.getDeclaredMethod(
220: "getReflectClass", 0);
221: return new ApplyExp(method, new Expression[] { arg0 });
222: }
223: if (type != null) {
224: Expression[] nargs = new Expression[] {
225: new QuoteExp(type), arg1 };
226: ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs);
227: nexp.setLine(exp);
228: exp = nexp;
229: }
230: } else
231: type = arg0.getType();
232: if (type instanceof ClassType && name != null) {
233: ClassType ctype = (ClassType) type;
234: ClassType caller = comp.curClass != null ? comp.curClass
235: : comp.mainClass;
236: Member part = lookupMember(ctype, name, caller);
237: if (part instanceof gnu.bytecode.Field) {
238: gnu.bytecode.Field field = (gnu.bytecode.Field) part;
239: ctype = field.getDeclaringClass();
240: int modifiers = field.getModifiers();
241: boolean isStaticField = (modifiers & Access.STATIC) != 0;
242: if (isStatic && !isStaticField)
243: return new ErrorExp(
244: "cannot access non-static field `" + name
245: + "' using `" + getName() + '\'',
246: comp);
247: if (caller != null
248: && !caller.isAccessible(ctype, modifiers))
249: return new ErrorExp("field " + ctype.getName()
250: + '.' + name + " is not accessible here",
251: comp);
252: }
253:
254: else if (part instanceof gnu.bytecode.Method) {
255: gnu.bytecode.Method method = (gnu.bytecode.Method) part;
256: ctype = method.getDeclaringClass();
257: int modifiers = method.getModifiers();
258: boolean isStaticMethod = method.getStaticFlag();
259: if (isStatic && !isStaticMethod)
260: return new ErrorExp(
261: "cannot call non-static getter method `"
262: + name + "' using `" + getName()
263: + '\'', comp);
264: if (caller != null
265: && !caller.isAccessible(ctype, modifiers))
266: return new ErrorExp("method " + method
267: + " is not accessible here", comp);
268: }
269: if (part != null) {
270: Expression[] nargs = new Expression[] { arg0,
271: new QuoteExp(part) };
272: ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs);
273: nexp.setLine(exp);
274: return nexp;
275: }
276: if (type != Type.pointer_type)
277: comp.error('e', "no slot `" + name + "' in "
278: + ctype.getName());
279: }
280: if (name != null && !(type instanceof ArrayType)) {
281: String fname = gnu.expr.Compilation
282: .mangleNameIfNeeded(name);
283: // So we can quickly check for "class" or "length".
284: // The name gets interned anyway when compiled.
285: fname = fname.intern();
286: String getName = ClassExp.slotToMethodName("get", name);
287: String isName = ClassExp.slotToMethodName("is", name);
288: ApplyExp nexp = new ApplyExp(
289: Invoke.invokeStatic,
290: new Expression[] {
291: QuoteExp
292: .getInstance("gnu.kawa.reflect.SlotGet"),
293: QuoteExp.getInstance("getSlotValue"),
294: isStatic ? QuoteExp.trueExp
295: : QuoteExp.falseExp, args[0],
296: QuoteExp.getInstance(name),
297: QuoteExp.getInstance(fname),
298: QuoteExp.getInstance(getName),
299: QuoteExp.getInstance(isName),
300: QuoteExp.getInstance(language) });
301: nexp.setLine(exp);
302: return ((InlineCalls) walker).walkApplyOnly(nexp);
303: }
304: return exp;
305: }
306:
307: public void compile(ApplyExp exp, Compilation comp, Target target) {
308: Expression[] args = exp.getArgs();
309: Expression arg0 = args[0];
310: Expression arg1 = args[1];
311: Language language = comp.getLanguage();
312: Type type = isStatic ? language.getTypeFor(arg0) : arg0
313: .getType();
314: CodeAttr code = comp.getCode();
315: if (type instanceof ClassType && arg1 instanceof QuoteExp) {
316: ClassType ctype = (ClassType) type;
317: Object part = ((QuoteExp) arg1).getValue();
318: if (part instanceof gnu.bytecode.Field) {
319: gnu.bytecode.Field field = (gnu.bytecode.Field) part;
320: int modifiers = field.getModifiers();
321: boolean isStaticField = (modifiers & Access.STATIC) != 0;
322: args[0].compile(comp, isStaticField ? Target.Ignore
323: : Target.pushValue(ctype));
324: if (isStaticField) {
325: boolean inlined = false;
326: /*
327: FIXME This isn't quite safe. We should only "inline"
328: the value if the field whose initializer is a constant
329: expression (JLS 2nd ed 15.28). We cannot determine this
330: using reflection instead we have to parse the .class file.
331:
332: Type ftype = field.getType();
333: if ((modifiers & Access.FINAL) != 0
334: && ftype instanceof PrimType)
335: {
336: // We inline int final fields.
337: // Other kinds of final fields are less obviously a win.
338: char sig = ftype.getSignature().charAt(0);
339: if (sig != 'F' && sig != 'D' && sig != 'J')
340: {
341: try
342: {
343: java.lang.reflect.Field rfield
344: = field.getReflectField();
345: int val = rfield.getInt(null);
346: code.emitPushInt(val);
347: inlined = true;
348: }
349: catch (Exception ex)
350: {
351: }
352: }
353: }
354: */
355: if (!inlined)
356: code.emitGetStatic(field);
357: } else
358: code.emitGetField(field);
359: Type ftype = field.getType();
360: Class fclass = ftype.getReflectClass();
361: if (fclass != null)
362: ftype = language.getTypeFor(fclass);
363: target.compileFromStack(comp, ftype);
364: return;
365: }
366: if (part instanceof Method) {
367: gnu.bytecode.Method method = (gnu.bytecode.Method) part;
368: int modifiers = method.getModifiers();
369: boolean isStaticMethod = method.getStaticFlag();
370: args[0].compile(comp, isStaticMethod ? Target.Ignore
371: : Target.pushValue(ctype));
372: if (isStaticMethod)
373: code.emitInvokeStatic(method);
374: else if (ctype.isInterface())
375: code.emitInvokeInterface(method);
376: else
377: code.emitInvokeVirtual(method);
378: target.compileFromStack(comp, method.getReturnType());
379: return;
380: }
381: }
382: String name = ClassMethods.checkName(arg1);
383: if (type instanceof ArrayType && "length".equals(name)
384: && !isStatic) {
385: args[0].compile(comp, Target.pushValue(type));
386: code.emitArrayLength();
387: target.compileFromStack(comp, LangPrimType.intType); // FIXME
388: return;
389: }
390: ApplyExp.compile(exp, comp, target);
391: }
392:
393: public Type getReturnType(Expression[] args) {
394: int nargs = args.length;
395: if (nargs == 2) {
396: Expression arg0 = args[0];
397: Expression arg1 = args[1];
398: if (arg1 instanceof QuoteExp) {
399: Object part = ((QuoteExp) arg1).getValue();
400: if (part instanceof gnu.bytecode.Field)
401: return ((gnu.bytecode.Field) part).getType();
402: if (part instanceof gnu.bytecode.Method)
403: return ((gnu.bytecode.Method) part).getReturnType();
404: if (!isStatic
405: && arg0.getType() instanceof ArrayType
406: && "length".equals(ClassMethods.checkName(arg1,
407: true)))
408: return gnu.kawa.lispexpr.LangPrimType.intType; // FIXME
409: }
410: }
411: return Type.pointer_type;
412: }
413:
414: public Procedure getSetter() {
415: return setter == null ? super .getSetter() : setter;
416: }
417:
418: /**
419: * Convenience method to make an Expression that gets the value of a field.
420: * @param value evaluates to object that has the named field
421: * @param fieldName name of field in value
422: * @return expression that get the name field from value
423: */
424: public static ApplyExp makeGetField(Expression value,
425: String fieldName) {
426: Expression[] args = new Expression[2];
427: args[0] = value;
428: args[1] = new QuoteExp(fieldName);
429: return new ApplyExp(gnu.kawa.reflect.SlotGet.field, args);
430: }
431: }
|