001: package gnu.kawa.reflect;
002:
003: import gnu.mapping.*;
004: import gnu.bytecode.*;
005: import gnu.lists.FString;
006: import gnu.expr.*;
007:
008: public class SlotSet extends Procedure3 implements CanInline,
009: Inlineable {
010: /** True if this is a "static-field" operation. */
011: boolean isStatic;
012:
013: /** Return value is the first argument, rather than void.
014: * Only if non-static. */
015: boolean returnSelf;
016:
017: public static final SlotSet set$Mnfield$Ex = new SlotSet(
018: "set-field!", false);
019: public static final SlotSet set$Mnstatic$Mnfield$Ex = new SlotSet(
020: "set-static-field!", true);
021: public static final SlotSet setFieldReturnObject = new SlotSet(
022: "set-field-return-object!", false);
023: static {
024: setFieldReturnObject.returnSelf = true;
025: }
026:
027: public SlotSet(String name, boolean isStatic) {
028: super (name);
029: this .isStatic = isStatic;
030: }
031:
032: public static void setField(Object obj, String name, Object value) {
033: apply(false, obj, name, value);
034: }
035:
036: public static void setStaticField(Object obj, String name,
037: Object value) {
038: apply(true, obj, name, value);
039: }
040:
041: public static void apply(boolean isStatic, Object obj, String name,
042: Object value) {
043: Language language = Language.getDefaultLanguage();
044: boolean illegalAccess = false;
045: String fname = gnu.expr.Compilation.mangleNameIfNeeded(name);
046: Class clas = isStatic ? SlotGet.coerceToClass(obj) : obj
047: .getClass();
048: try {
049: java.lang.reflect.Field field = clas.getField(fname);
050: Class ftype = field.getType();
051: field.set(obj, language.coerceFromObject(ftype, value));
052: return;
053: } catch (java.lang.NoSuchFieldException ex) {
054: } catch (IllegalAccessException ex) {
055: illegalAccess = true;
056: }
057:
058: // Try looking for a method "setFname" instead.
059: // First look for "getName" or "isName", to get the "field type".
060: try {
061: java.lang.reflect.Method getmethod = null;
062:
063: try {
064: String getName = ClassExp.slotToMethodName("get", name);
065: getmethod = clas.getMethod(getName, SlotGet.noClasses);
066: } catch (Exception getEx) {
067: String getName = ClassExp.slotToMethodName("is", name);
068: getmethod = clas.getMethod(getName, SlotGet.noClasses);
069: }
070:
071: String setName = ClassExp.slotToMethodName("set", name);
072: Class[] setArgTypes = new Class[1];
073: setArgTypes[0] = getmethod.getReturnType();
074: java.lang.reflect.Method setmethod = clas.getMethod(
075: setName, setArgTypes);
076: Object[] args = new Object[1];
077: args[0] = language.coerceFromObject(setArgTypes[0], value);
078: setmethod.invoke(obj, args);
079: return;
080: } catch (java.lang.reflect.InvocationTargetException ex) {
081: throw WrappedException
082: .wrapIfNeeded(ex.getTargetException());
083: } catch (IllegalAccessException ex) {
084: illegalAccess = true;
085: } catch (java.lang.NoSuchMethodException ex) {
086: }
087:
088: if (illegalAccess)
089: throw new RuntimeException("illegal access for field "
090: + name);
091: else
092: throw new RuntimeException("no such field " + name + " in "
093: + clas.getName());
094: }
095:
096: public Object apply3(Object obj, Object fname, Object value) {
097: apply(isStatic, obj, (String) fname, value);
098: return returnSelf ? obj : Values.empty;
099: }
100:
101: public static Member lookupMember(ClassType clas, String name,
102: ClassType caller) {
103: gnu.bytecode.Field field = clas.getField(Compilation
104: .mangleNameIfNeeded(name), -1);
105: if (field != null) {
106: if (caller == null)
107: caller = Type.pointer_type;
108: if (caller.isAccessible(field.getDeclaringClass(), field
109: .getModifiers()))
110: return field;
111: }
112:
113: String setName = ClassExp.slotToMethodName("set", name);
114: gnu.bytecode.Method method = clas.getMethod(setName,
115: new Type[1]);
116: if (method == null)
117: return field;
118: else
119: return method;
120: }
121:
122: static void compileSet(Procedure this Proc, ClassType ctype,
123: Expression valArg, Object part, Compilation comp) {
124: CodeAttr code = comp.getCode();
125: Language language = comp.getLanguage();
126: boolean isStatic = this Proc instanceof SlotSet
127: && ((SlotSet) this Proc).isStatic;
128: if (part instanceof gnu.bytecode.Field) {
129: gnu.bytecode.Field field = (gnu.bytecode.Field) part;
130: boolean isStaticField = field.getStaticFlag();
131: Type ftype = language.getLangTypeFor(field.getType());
132: if (isStatic && !isStaticField)
133: comp.error('e', ("cannot access non-static field `"
134: + field.getName() + "' using `"
135: + this Proc.getName() + '\''));
136: valArg.compile(comp, CheckedTarget.getInstance(ftype));
137: if (isStaticField)
138: code.emitPutStatic(field);
139: else
140: code.emitPutField(field);
141: return;
142: }
143: if (part instanceof gnu.bytecode.Method) {
144: gnu.bytecode.Method method = (gnu.bytecode.Method) part;
145: boolean isStaticMethod = method.getStaticFlag();
146: if (isStatic && !isStaticMethod)
147: comp.error('e',
148: "cannot call non-static getter method `"
149: + method.getName() + "' using `"
150: + this Proc.getName() + '\'');
151: Type[] setArgTypes = method.getParameterTypes();
152: valArg.compile(comp, CheckedTarget.getInstance(language
153: .getLangTypeFor(setArgTypes[0])));
154: if (isStaticMethod)
155: code.emitInvokeStatic(method);
156: else if (ctype.isInterface())
157: code.emitInvokeInterface(method);
158: else
159: code.emitInvokeVirtual(method);
160: return;
161: }
162: }
163:
164: public Expression inline(ApplyExp exp, ExpWalker walker) {
165: // Unlike, for SlotGet, we do the field-lookup at compile time
166: // rather than inline time. The main reason is that optimizing
167: // (set! CLASS-OR-OBJECT:FIELD-NAME VALUE) is tricky, since (currently)
168: // afte we've inlined setter, this method doesn't get called.
169: if (isStatic && walker.getCompilation().mustCompile)
170: return Invoke.inlineClassName(exp, 0, (InlineCalls) walker);
171: else
172: return exp;
173: }
174:
175: public void compile(ApplyExp exp, Compilation comp, Target target) {
176: Expression[] args = exp.getArgs();
177: int nargs = args.length;
178: if (nargs != 3) {
179: String msg = nargs < 3 ? "too few" : "too many";
180: comp.error('e', msg + " arguments to `" + getName() + '\'');
181: comp.compileConstant(null, target);
182: return;
183: }
184: Expression arg0 = args[0];
185: Expression arg1 = args[1];
186: Expression value = args[2];
187: Type type = isStatic ? kawa.standard.Scheme.exp2Type(arg0)
188: : arg0.getType();
189: Member part = null;
190: if (type instanceof ClassType && arg1 instanceof QuoteExp) {
191: Object val1 = ((QuoteExp) arg1).getValue();
192: ClassType ctype = (ClassType) type;
193: String name;
194: ClassType caller = comp.curClass != null ? comp.curClass
195: : comp.mainClass;
196: if (val1 instanceof String || val1 instanceof FString
197: || val1 instanceof Symbol) {
198: name = val1.toString();
199: part = lookupMember(ctype, name, caller);
200: if (part == null && type != Type.pointer_type)
201: comp.error('e', "no slot `" + name + "' in "
202: + ctype.getName());
203: } else if (val1 instanceof Member) {
204: // Inlining (make <type> field: value) creates calls to
205: // setFieldReturnObject whose 2nd arg is a Field or Method.
206: part = (Member) val1;
207: name = part.getName();
208: } else
209: name = null;
210:
211: if (part != null) {
212: int modifiers = part.getModifiers();
213: ClassType ptype = part.getDeclaringClass();
214: boolean isStaticField = (modifiers & Access.STATIC) != 0;
215: if (caller != null
216: && !caller.isAccessible(ptype, modifiers))
217: comp.error('e', "slot '" + name + "' in "
218: + ptype.getName() + " not accessible here");
219: args[0].compile(comp, isStaticField ? Target.Ignore
220: : Target.pushValue(ctype));
221: if (returnSelf)
222: comp.getCode().emitDup(ctype);
223: compileSet(this , ctype, args[2], part, comp);
224: if (returnSelf)
225: target.compileFromStack(comp, ctype);
226: else
227: comp.compileConstant(Values.empty, target);
228: return;
229: }
230: }
231: ApplyExp.compile(exp, comp, target);
232: }
233:
234: public Type getReturnType(Expression[] args) {
235: if (returnSelf && args.length == 3)
236: return args[0].getType();
237: return Type.void_type;
238: }
239: }
|