001: // Copyright (c) 2000, 2001 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.expr;
005:
006: import gnu.bytecode.*;
007: import gnu.kawa.reflect.OccurrenceType;
008:
009: /**
010: * A Target which is some variable that implements gnu.lists.Consumer.
011: */
012:
013: public class ConsumerTarget extends Target {
014: Variable consumer;
015: boolean isContextTarget;
016:
017: public ConsumerTarget(Variable consumer) {
018: this .consumer = consumer;
019: }
020:
021: public Variable getConsumerVariable() {
022: return consumer;
023: }
024:
025: /** True iff this target is the current CallContext's current Consumer. */
026: public final boolean isContextTarget() {
027: return isContextTarget;
028: }
029:
030: /** Make a Target that uses the current CallContext's current Consumer. */
031: public static Target makeContextTarget(Compilation comp) {
032: CodeAttr code = comp.getCode();
033: comp.loadCallContext();
034: code.emitGetField(Compilation.typeCallContext
035: .getDeclaredField("consumer"));
036: Scope scope = code.getCurrentScope();
037: Variable result = scope.addVariable(code,
038: Compilation.typeConsumer, "$result");
039: code.emitStore(result);
040: ConsumerTarget target = new ConsumerTarget(result);
041: target.isContextTarget = true;
042: return target;
043: }
044:
045: /** Compile an expression using a temporary Consumer, if needed. */
046: public static void compileUsingConsumer(Expression exp,
047: Compilation comp, Target target) {
048: if (target instanceof ConsumerTarget
049: || target instanceof IgnoreTarget)
050: exp.compile(comp, target);
051: else {
052: ClassType typeValues = Compilation.typeValues;
053: compileUsingConsumer(exp, comp, target, typeValues
054: .getDeclaredMethod("make", 0), typeValues
055: .getDeclaredMethod("canonicalize", 0));
056: }
057: }
058:
059: public static void compileUsingConsumer(Expression exp,
060: Compilation comp, Target target, Method makeMethod,
061: Method resultMethod) {
062: CodeAttr code = comp.getCode();
063: Scope scope = code.pushScope();
064: Type ctype;
065: if (makeMethod.getName() == "<init>") {
066: ClassType cltype = makeMethod.getDeclaringClass();
067: ctype = cltype;
068: code.emitNew(cltype);
069: code.emitDup(ctype);
070: code.emitInvoke(makeMethod);
071: } else {
072: ctype = makeMethod.getReturnType();
073: code.emitInvokeStatic(makeMethod);
074: }
075: Variable consumer = scope.addVariable(code, ctype, null);
076: ConsumerTarget ctarget = new ConsumerTarget(consumer);
077: code.emitStore(consumer);
078: exp.compile(comp, ctarget);
079: code.emitLoad(consumer);
080: if (resultMethod != null)
081: code.emitInvoke(resultMethod);
082: code.popScope();
083: target.compileFromStack(comp, resultMethod == null ? ctype
084: : resultMethod.getReturnType());
085: }
086:
087: public void compileFromStack(Compilation comp, Type stackType) {
088: compileFromStack(comp, stackType, -1);
089: }
090:
091: /** Write stack value to Consumer.
092: * @param consumerPushed if -1, then Consumer has not been pushed;
093: * if 1, Consumer was pushed before value, and value is a known singleton;
094: * if 0, Consumer was pushed before value, otherwise.
095: */
096: void compileFromStack(Compilation comp, Type stackType,
097: int consumerPushed) {
098: CodeAttr code = comp.getCode();
099: String methodName = null;
100: Method method = null;
101: boolean islong = false;
102: stackType = stackType.getImplementationType();
103: char sig;
104: if (stackType instanceof PrimType) {
105: sig = stackType.getSignature().charAt(0);
106: switch (sig) {
107: case 'B':
108: case 'S':
109: case 'I':
110: methodName = "writeInt";
111: break;
112: case 'J':
113: methodName = "writeLong";
114: islong = true;
115: break;
116: case 'F':
117: methodName = "writeFloat";
118: break;
119: case 'D':
120: methodName = "writeDouble";
121: islong = true;
122: break;
123: case 'C':
124: methodName = "append";
125: break;
126: case 'Z':
127: methodName = "writeBoolean";
128: break;
129: case 'V':
130: return;
131: }
132: } else {
133: sig = '\0';
134: if (consumerPushed == 1
135: || OccurrenceType.itemCountIsOne(stackType))
136: methodName = "writeObject";
137: else {
138: method = (Compilation.typeValues.getDeclaredMethod(
139: "writeValues", 2));
140: code.emitLoad(consumer);
141: if (consumerPushed == 0)
142: code.emitSwap();
143: code.emitInvokeStatic(method);
144: return;
145: }
146: }
147: if (consumerPushed >= 0)
148: ;
149: else if (islong) {
150: code.pushScope();
151: Variable temp = code.addLocal(stackType);
152: code.emitStore(temp);
153: code.emitLoad(consumer);
154: code.emitLoad(temp);
155: code.popScope();
156: } else {
157: code.emitLoad(consumer);
158: code.emitSwap();
159: }
160: if (method == null && methodName != null)
161: method = Compilation.typeConsumer.getDeclaredMethod(
162: methodName, 1);
163: if (method != null)
164: code.emitInvokeInterface(method);
165: if (sig == 'C')
166: code.emitPop(1); // Pop consumer result.
167: }
168:
169: public boolean compileWrite(Expression exp, Compilation comp) {
170: Type stackType = exp.getType();
171: Type implType = stackType.getImplementationType();
172: if ((implType instanceof PrimType && !implType.isVoid())
173: || gnu.kawa.reflect.OccurrenceType
174: .itemCountIsOne(implType)) {
175: // Optimization to avoid a 'swap'.
176: comp.getCode().emitLoad(this .consumer);
177: Target starget = StackTarget.getInstance(implType);
178: exp.compile(comp, starget);
179: compileFromStack(comp, implType, 1);
180: return true;
181: }
182: return false;
183: }
184:
185: public Type getType() {
186: return Compilation.scmSequenceType;
187: }
188: }
|