001: package gnu.kawa.functions;
002:
003: import gnu.bytecode.*;
004: import gnu.mapping.*;
005: import gnu.kawa.reflect.*;
006: import gnu.expr.*;
007: import java.io.*;
008: import kawa.lang.Translator;
009:
010: /** Procedure to get the value of a named component of an object. */
011:
012: public class GetNamedPart extends Procedure2 implements HasSetter,
013: CanInline {
014: public static final GetNamedPart getNamedPart = new GetNamedPart();
015:
016: /** PREFIX:<> is equivalent to the ClassType bound to PREFIX. */
017: public static final String CLASSTYPE_FOR = "<>";
018:
019: /** Pseudo-method-name for the cast operation. */
020: public static final String CAST_METHOD_NAME = "@";
021:
022: /** Pseudo-method-name for class-membership-test (instanceof) operation. */
023: public static final String INSTANCEOF_METHOD_NAME = "instance?";
024:
025: public static String combineName(Expression part1, Expression part2) {
026: String name1;
027: Object name2;
028: if (part2 instanceof QuoteExp
029: && (name2 = ((QuoteExp) part2).getValue()) instanceof String
030: && ((part1 instanceof ReferenceExp && (name1 = ((ReferenceExp) part1)
031: .getSimpleName()) != null) || (part1 instanceof GetNamedExp && (name1 = ((GetNamedExp) part1).combinedName) != null)))
032: return (name1 + ':' + name2).intern();
033: return null;
034: }
035:
036: public static Expression makeExp(Expression clas, Expression member) {
037: ReferenceExp rexp;
038: String combinedName = combineName(clas, member);
039: Environment env = Environment.getCurrent();
040: if (combinedName != null) {
041: Translator tr = (Translator) Compilation.getCurrent();
042: Declaration decl = tr.lexical
043: .lookup(combinedName, false/*FIXME*/);
044: if (!Declaration.isUnknown(decl))
045: return new ReferenceExp(decl);
046:
047: Symbol symbol = env.defaultNamespace().lookup(combinedName);
048: Object property = null; // FIXME?
049: if (symbol != null && env.isBound(symbol, property))
050: return new ReferenceExp(combinedName);
051: }
052: if (clas instanceof ReferenceExp
053: && (rexp = (ReferenceExp) clas).isUnknown()) {
054: Object rsym = rexp.getSymbol();
055: Symbol sym = rsym instanceof Symbol ? (Symbol) rsym : env
056: .getSymbol(rsym.toString());
057: if (env.get(sym, null) == null) {
058: String name = rexp.getName();
059: try {
060: Class cl = ClassType.getContextClass(name);
061: clas = QuoteExp.getInstance(Type.make(cl));
062: } catch (Throwable ex) {
063: }
064: }
065: }
066: Expression[] args = { clas, member };
067: GetNamedExp exp = new GetNamedExp(args);
068: exp.combinedName = combinedName;
069: return exp;
070: }
071:
072: public static Expression makeExp(Expression clas, String member) {
073: return makeExp(clas, new QuoteExp(member));
074: }
075:
076: public static Expression makeExp(Type type, String member) {
077: return makeExp(new QuoteExp(type), new QuoteExp(member));
078: }
079:
080: public Expression inline(ApplyExp exp, ExpWalker walker) {
081: Expression[] args = exp.getArgs();
082: if (args.length != 2 || !(args[1] instanceof QuoteExp)
083: || !(exp instanceof GetNamedExp))
084: return exp;
085: Expression context = args[0];
086: Declaration decl = null;
087: if (context instanceof ReferenceExp) {
088: ReferenceExp rexp = (ReferenceExp) context;
089: if ("*".equals(rexp.getName()))
090: return GetNamedInstancePart.makeExp(args[1]);
091: decl = rexp.getBinding();
092: }
093:
094: String mname = ((QuoteExp) args[1]).getValue().toString();
095: Type type = context.getType();
096: boolean isInstanceOperator = context == QuoteExp.nullExp;
097: Compilation comp = walker.getCompilation();
098: Language language = comp.getLanguage();
099: Type typeval = language.getTypeFor(context, false);
100: ClassType caller = comp == null ? null
101: : comp.curClass != null ? comp.curClass
102: : comp.mainClass;
103: GetNamedExp nexp = (GetNamedExp) exp;
104:
105: if (typeval instanceof Type) {
106: if (mname.equals(CLASSTYPE_FOR))
107: return new QuoteExp(typeval);
108:
109: if (typeval instanceof ObjectType) {
110: if (mname.equals("new"))
111: return nexp.setProcedureKind('N');
112: if (mname.equals(INSTANCEOF_METHOD_NAME))
113: return nexp.setProcedureKind('I');
114: if (mname.equals(CAST_METHOD_NAME))
115: return nexp.setProcedureKind('C');
116: }
117: }
118: if (typeval instanceof ClassType) {
119: if (mname.length() > 1 && mname.charAt(0) == '.') {
120: // The following would also work:
121: // return nexp.setProcedureKind('D');
122: // However, it makes optimzing the 'setter' case harder.
123: return new QuoteExp(new NamedPart(typeval, mname, 'D'));
124: }
125: if (Invoke.checkKnownClass(typeval, comp) < 0)
126: return exp;
127: PrimProcedure[] methods = ClassMethods.getMethods(
128: (ClassType) typeval, Compilation.mangleName(mname),
129: '\0', caller, language);
130: if (methods != null && methods.length > 0) {
131: nexp.methods = methods;
132: return nexp.setProcedureKind('S');
133: }
134: ApplyExp aexp = new ApplyExp(SlotGet.staticField, args);
135: aexp.setLine(exp);
136: return ((InlineCalls) walker).walkApplyOnly(aexp);
137:
138: }
139: if (typeval != null) {
140:
141: }
142:
143: /*
144: if (type.isSubtype(Compilation.typeValues))
145: {
146: // FIXME
147: }
148: */
149:
150: if (type.isSubtype(Compilation.typeClassType)
151: || type.isSubtype(Type.java_lang_Class_type))
152: // The container evaluates to a class (so we should look for a static
153: // field or method), but we don't know which class at compile-time.
154: // However, we should still optimize it somewhat, above. FIXME.
155: return exp;
156:
157: if (type instanceof ObjectType) {
158: ObjectType otype = (ObjectType) type;
159: ClassType ctype = type instanceof ClassType ? (ClassType) type
160: : Type.pointer_type;
161: PrimProcedure[] methods = ClassMethods.getMethods(otype,
162: Compilation.mangleName(mname), 'V', caller,
163: language);
164: if (methods != null && methods.length > 0) {
165: nexp.methods = methods;
166: return nexp.setProcedureKind('M');
167: }
168: Member part = SlotGet.lookupMember(ctype, mname, caller);
169: if (part != null
170: || (mname.equals("length") && type instanceof ArrayType)) {
171: // FIXME: future kludge to avoid re-doing SlotGet.getField.
172: // args = new Expression[] { context, new QuoteExp(part) });
173: ApplyExp aexp = new ApplyExp(SlotGet.field, args);
174: aexp.setLine(exp);
175: return ((InlineCalls) walker).walkApplyOnly(aexp);
176: }
177:
178: if (type.isSubtype(typeHasNamedParts)) {
179: Object val;
180: if (decl != null
181: && (val = Declaration.followAliases(decl)
182: .getConstantValue()) != null) {
183: HasNamedParts value = (HasNamedParts) val;
184: if (value.isConstant(mname)) {
185: val = value.get(mname);
186: return QuoteExp.getInstance(val);
187: }
188: }
189: return new ApplyExp(typeHasNamedParts
190: .getDeclaredMethod("get", 1), args)
191: .setLine(exp);
192: }
193: }
194:
195: if (comp.getBooleanOption("warn-invoke-unknown-method",
196: !comp.immediate))
197: comp.error('w', "no known slot '" + mname + "' in "
198: + type.getName());
199: return exp;
200: }
201:
202: static final ClassType typeHasNamedParts = ClassType
203: .make("gnu.mapping.HasNamedParts");
204:
205: public Object apply2(Object container, Object part)
206: throws Throwable {
207: if (container instanceof Values) {
208: Object[] values = ((Values) container).getValues();
209: Values result = new Values();
210: for (int i = 0; i < values.length; i++) {
211: Values.writeValues(apply2(values[i], part), result);
212: }
213: return result.canonicalize();
214: }
215: Symbol sym;
216: if (part instanceof Symbol)
217: sym = (Symbol) part;
218: else
219: sym = Namespace.EmptyNamespace.getSymbol(part.toString()
220: .intern());
221: return getNamedPart(container, sym);
222: }
223:
224: public static Object getTypePart(Type type, String name)
225: throws Throwable {
226: if (name.equals(CLASSTYPE_FOR))
227: return type;
228:
229: if (type instanceof ObjectType) {
230: if (name.equals(INSTANCEOF_METHOD_NAME))
231: return new NamedPart(type, name, 'I');
232: if (name.equals(CAST_METHOD_NAME))
233: return new NamedPart(type, name, 'C');
234: if (name.equals("new"))
235: return new NamedPart(type, name, 'N');
236: if (name.equals(".length")
237: || (name.length() > 1 && name.charAt(0) == '.' && type instanceof ClassType))
238: return new NamedPart(type, name, 'D');
239: }
240:
241: if (type instanceof ClassType) {
242: try {
243: return gnu.kawa.reflect.SlotGet.staticField(type, name);
244: } catch (Throwable ex) {
245: // FIXME!
246: }
247: return ClassMethods.apply(ClassMethods.classMethods, type,
248: name);
249: }
250: return getMemberPart(type, name);
251: }
252:
253: public static Object getNamedPart(Object container, Symbol part)
254: throws Throwable {
255: String name = part.getName();
256: if (container instanceof HasNamedParts)
257: return ((HasNamedParts) container).get(name);
258: if (container instanceof Class)
259: container = (ClassType) Type.make((Class) container);
260: if (container instanceof Type)
261: return getTypePart((Type) container, name);
262: return getMemberPart(container, part.toString());
263: }
264:
265: public static Object getMemberPart(Object container, String name)
266: throws Throwable {
267: try {
268: return gnu.kawa.reflect.SlotGet.field(container, name);
269: } catch (Throwable ex) {
270: // FIXME!
271: }
272: MethodProc methods = ClassMethods.apply((ClassType) ClassType
273: .make(container.getClass()), Compilation
274: .mangleName(name), '\0', Language.getDefaultLanguage());
275: if (methods != null)
276: return new NamedPart(container, name, 'M', methods);
277: throw new RuntimeException("no part '" + name + "' in "
278: + container);
279: }
280:
281: public Procedure getSetter() {
282: return SetNamedPart.setNamedPart;
283: }
284: }
285:
286: class GetNamedExp extends ApplyExp {
287: /*
288: * 'N' - new (make) - if methodName is "new".
289: * 'I' - instance of - if methodName is INSTANCEOF_METHOD_NAME.
290: * 'C' - cast - if methodName is CAST_METHOD_NAME.
291: * 'T' - type - if methodName is CLASSTYPE_FOR
292: * 'M' - non-static method
293: * 'S' - static method
294: * 'D' - if methodname starts with '.'
295: */
296: char kind;
297: PrimProcedure[] methods;
298:
299: public String combinedName;
300:
301: public void apply(CallContext ctx) throws Throwable {
302: if (combinedName != null) {
303: Environment env = ctx.getEnvironment();
304: Symbol sym = env.getSymbol(combinedName);
305: Object unb = gnu.mapping.Location.UNBOUND;
306: Object property = null; // FIXME?
307: Object value = env.get(sym, property, unb);
308: if (value != unb) {
309: ctx.writeValue(value);
310: return;
311: }
312: }
313: super .apply(ctx);
314: }
315:
316: public GetNamedExp(Expression[] args) {
317: super (GetNamedPart.getNamedPart, args);
318: }
319:
320: protected GetNamedExp setProcedureKind(char kind) {
321: // Called from GetNamedPart.inline when the expression evaluates to a
322: // procedure that takes (at least) a 'this' parameter. If the
323: // expression is in turn used in function call position it is normally
324: // the first argment to ApplyToArgs, so setting the type to typeProcedure
325: // allows ApplyToArgs.inline to be optimized away, and then later
326: // the inline method in the GetNamedExp class can get called.
327: this .type = Compilation.typeProcedure;
328: this .kind = kind;
329: return this ;
330: }
331:
332: public Expression inline(ApplyExp exp, InlineCalls walker,
333: Declaration decl) {
334: Expression[] pargs = getArgs();
335: Expression context = pargs[0];
336: Expression[] args = exp.getArgs();
337: Expression[] xargs;
338: switch (kind) {
339: case 'M':
340: decl = invokeDecl;
341: xargs = new Expression[args.length + 2];
342: xargs[0] = pargs[0];
343: xargs[1] = pargs[1];
344: System.arraycopy(args, 0, xargs, 2, args.length);
345: break;
346: case 'N': // new
347: decl = makeDecl;
348: xargs = new Expression[args.length + 1];
349: System.arraycopy(args, 0, xargs, 1, args.length);
350: xargs[0] = context;
351: break;
352: case 'I': // instance-of
353: decl = instanceOfDecl;
354: xargs = new Expression[args.length + 1];
355: System.arraycopy(args, 1, xargs, 2, args.length - 1);
356: xargs[0] = args[0];
357: xargs[1] = context;
358: break;
359: case 'C': // cast
360: decl = castDecl;
361: xargs = new Expression[args.length + 1];
362: System.arraycopy(args, 1, xargs, 2, args.length - 1);
363: xargs[0] = context;
364: xargs[1] = args[0];
365: break;
366: case 'S': // invoke-static
367: decl = invokeStaticDecl;
368: xargs = new Expression[args.length + 2];
369: xargs[0] = context;
370: xargs[1] = pargs[1];
371: System.arraycopy(args, 0, xargs, 2, args.length);
372: break;
373: default:
374: return exp;
375: }
376: ApplyExp result = new ApplyExp(new ReferenceExp(decl), xargs);
377: result.setLine(exp);
378: return walker.walkApplyOnly(result);
379: }
380:
381: public boolean side_effects() {
382: // The actual GetNamedExp that returns a method reference doesn't
383: // have side-effects - though applying tha result does.
384: if (kind == 'S' || kind == 'N' || kind == 'C' || kind == 'I')
385: return false;
386: if (kind == 'M')
387: return getArgs()[0].side_effects();
388: return true;
389: }
390:
391: static final Declaration fieldDecl = Declaration
392: .getDeclarationFromStatic("gnu.kawa.reflect.SlotGet",
393: "field");
394:
395: static final Declaration staticFieldDecl = Declaration
396: .getDeclarationFromStatic("gnu.kawa.reflect.SlotGet",
397: "staticField");
398:
399: static final Declaration makeDecl = Declaration
400: .getDeclarationFromStatic("gnu.kawa.reflect.Invoke", "make");
401:
402: static final Declaration invokeDecl = Declaration
403: .getDeclarationFromStatic("gnu.kawa.reflect.Invoke",
404: "invoke");
405:
406: static final Declaration invokeStaticDecl = Declaration
407: .getDeclarationFromStatic("gnu.kawa.reflect.Invoke",
408: "invokeStatic");
409:
410: static final Declaration instanceOfDecl = Declaration
411: .getDeclarationFromStatic("kawa.standard.Scheme",
412: "instanceOf");
413:
414: static final Declaration castDecl = Declaration
415: .getDeclarationFromStatic("gnu.kawa.functions.Convert",
416: "as");
417: }
418:
419: class NamedPart extends ProcedureN implements HasSetter,
420: Externalizable, CanInline {
421: Object container;
422: Object member;
423: char kind;
424: MethodProc methods;
425:
426: public NamedPart(Object container, Object member, char kind) {
427: this .container = container;
428: this .member = member;
429: this .kind = kind;
430: }
431:
432: public NamedPart(Object container, String mname, char kind,
433: MethodProc methods) {
434: this .container = container;
435: this .methods = methods;
436: this .member = mname;
437: this .kind = kind;
438: }
439:
440: public int numArgs() {
441: if (kind == 'I' || kind == 'C')
442: return 0x1001;
443: if (kind == 'D')
444: return 0x1000;
445: return 0xfffff000;
446: }
447:
448: public Expression inline(ApplyExp exp, ExpWalker walker) {
449: Expression[] args = exp.getArgs();
450: switch (kind) {
451: case 'D':
452: String fname = member.toString().substring(1);
453: Expression[] xargs = new Expression[2];
454: xargs[1] = QuoteExp.getInstance(fname);
455: SlotGet proc;
456: if (args.length > 0) {
457: xargs[0] = Convert.makeCoercion(args[0], new QuoteExp(
458: container));
459: proc = SlotGet.field;
460: } else {
461: xargs[0] = QuoteExp.getInstance(container);
462: proc = SlotGet.staticField;
463: }
464: ApplyExp aexp = new ApplyExp(proc, xargs);
465: aexp.setLine(exp);
466: return ((InlineCalls) walker).walkApplyOnly(aexp);
467: }
468: return exp;
469: }
470:
471: public void apply(CallContext ctx) throws Throwable {
472: apply(ctx.getArgs(), ctx);
473: }
474:
475: public void apply(Object[] args, CallContext ctx) throws Throwable {
476: // Optimization, so that output from the
477: // method is sent directly to ctx.consumer, rather than reified.
478: if (kind == 'S')
479: methods.checkN(args, ctx);
480: else if (kind == 'M') {
481: int nargs = args.length;
482: Object[] xargs = new Object[nargs + 1];
483: xargs[0] = container;
484: System.arraycopy(args, 0, xargs, 1, nargs);
485: methods.checkN(xargs, ctx);
486: } else
487: ctx.writeValue(this .applyN(args));
488: }
489:
490: public Object applyN(Object[] args) throws Throwable {
491: Object[] xargs;
492:
493: switch (kind) {
494: case 'I':
495: return kawa.standard.Scheme.instanceOf.apply2(args[0],
496: container);
497: case 'C':
498: return gnu.kawa.functions.Convert.as.apply2(container,
499: args[0]);
500: case 'N':
501: xargs = new Object[args.length + 1];
502: xargs[0] = container;
503: System.arraycopy(args, 0, xargs, 1, args.length);
504: return Invoke.make.applyN(xargs);
505: case 'S':
506: return methods.applyN(args);
507: case 'M':
508: xargs = new Object[args.length + 1];
509: xargs[0] = container;
510: System.arraycopy(args, 0, xargs, 1, args.length);
511: return methods.applyN(xargs);
512: case 'D':
513: String fname = member.toString().substring(1);
514: if (args.length == 0)
515: return SlotGet
516: .staticField((ClassType) container, fname);
517: else
518: return SlotGet.field(((Type) container)
519: .coerceFromObject(args[0]), fname);
520: }
521: throw new Error("unknown part " + member + " in " + container);
522: }
523:
524: public Procedure getSetter() {
525: if (kind == 'D')
526: return new NamedPartSetter(this );
527: else
528: throw new RuntimeException("procedure '" + getName()
529: + "' has no setter");
530: }
531:
532: public void set0(Object value) throws Throwable {
533: switch (kind) {
534: case 'D':
535: String fname = member.toString().substring(1);
536: SlotSet.setStaticField((ClassType) container, fname, value);
537: return;
538: default:
539: throw new Error("invalid setter for " + this );
540: }
541: }
542:
543: public void set1(Object object, Object value) throws Throwable {
544: switch (kind) {
545: case 'D':
546: String fname = member.toString().substring(1);
547: object = ((Type) container).coerceFromObject(object);
548: SlotSet.setField(object, fname, value);
549: return;
550: default:
551: throw new Error("invalid setter for " + this );
552: }
553: }
554:
555: public void writeExternal(ObjectOutput out) throws IOException {
556: out.writeObject(container);
557: out.writeObject(member);
558: out.writeChar(kind);
559: }
560:
561: public void readExternal(ObjectInput in) throws IOException,
562: ClassNotFoundException {
563: kind = in.readChar();
564: container = (Procedure) in.readObject();
565: member = (Procedure) in.readObject();
566: }
567: }
568:
569: class NamedPartSetter extends gnu.mapping.Setter implements
570: Externalizable, CanInline {
571: public NamedPartSetter(NamedPart getter) {
572: super (getter);
573: }
574:
575: public int numArgs() {
576: if (((NamedPart) getter).kind == 'D')
577: return 0x2001;
578: return 0xfffff000;
579: }
580:
581: public Expression inline(ApplyExp exp, ExpWalker walker) {
582: NamedPart get = (NamedPart) this .getter;
583: if (get.kind == 'D') {
584: Expression[] xargs = new Expression[3];
585: xargs[1] = QuoteExp.getInstance(get.member.toString()
586: .substring(1));
587: xargs[2] = exp.getArgs()[0];
588: SlotSet proc;
589: if (exp.getArgCount() == 1) {
590: xargs[0] = QuoteExp.getInstance(get.container);
591: proc = SlotSet.set$Mnstatic$Mnfield$Ex;
592: } else if (exp.getArgCount() == 2) {
593: xargs[0] = Convert.makeCoercion(exp.getArgs()[0],
594: new QuoteExp(get.container));
595: proc = SlotSet.set$Mnfield$Ex;
596: } else
597: return exp;
598: ApplyExp aexp = new ApplyExp(proc, xargs);
599: aexp.setLine(exp);
600: return ((InlineCalls) walker).walkApplyOnly(aexp);
601: }
602: return exp;
603: }
604:
605: public void writeExternal(ObjectOutput out) throws IOException {
606: out.writeObject(getter);
607: }
608:
609: public void readExternal(ObjectInput in) throws IOException,
610: ClassNotFoundException {
611: getter = (Procedure) in.readObject();
612: }
613: }
|