1 // Copyright (c) 2000, 2001 Per M.A. Bothner.
2 // This is free software;  for terms and warranty disclaimer see ./COPYING.
3 
4 package gnu.expr;
5 import gnu.bytecode.*;
6 import gnu.kawa.reflect.OccurrenceType;
7 import gnu.kawa.reflect.SingletonType;
8 import gnu.kawa.lispexpr.LangPrimType;
9 
10 /**
11  * A Target which is some variable that implements gnu.lists.Consumer.
12  */
13 
14 public class ConsumerTarget extends Target
15 {
16   Variable consumer;
17   boolean isContextTarget;
18   Type type;
19 
20     public final static ConsumerTarget contextInstance
21         = new ConsumerTarget(null);
22 
ConsumerTarget(Variable consumer)23   public ConsumerTarget(Variable consumer)
24   {
25     this.consumer = consumer;
26     this.type = Type.objectType;
27   }
28 
ConsumerTarget(Variable consumer, Type type)29   public ConsumerTarget(Variable consumer, Type type)
30   {
31     this.consumer = consumer;
32     this.type = type;
33   }
34 
35     private ConsumerTarget singleTarget;
36 
37     /** Get equivalent target but which only accepts a single item. */
getSingleTarget()38     public ConsumerTarget getSingleTarget() {
39         if (singleTarget == null) {
40             Type base;
41             if (! (type instanceof OccurrenceType)
42                 || ! (OccurrenceType.itemCountIsOne
43                       (base = ((OccurrenceType) type).getBase())))
44                 base = SingletonType.getInstance();
45             singleTarget = new ConsumerTarget(consumer, base);
46             singleTarget.isContextTarget = this.isContextTarget;
47         }
48         return singleTarget;
49     }
50 
getConsumerVariable()51   public Variable getConsumerVariable() { return consumer; }
52 
53   /** True iff this target is the current CallContext's current Consumer. */
isContextTarget()54   public final boolean isContextTarget () { return isContextTarget; }
55 
56   /** Make a Target that uses the current CallContext's current Consumer. */
makeContextTarget(Compilation comp, Type type)57   public static Target makeContextTarget (Compilation comp, Type type)
58   {
59     CodeAttr code = comp.getCode();
60     comp.loadCallContext();
61     code.emitGetField(Compilation.typeCallContext
62 		      .getDeclaredField("consumer"));
63     Scope scope = code.getCurrentScope();
64     Variable result
65       = scope.addVariable(code, Compilation.typeConsumer, "$result");
66     code.emitStore(result);
67     ConsumerTarget target = new ConsumerTarget(result, type);
68     target.isContextTarget = true;
69     return target;
70   }
71 
compileUsingValues(Expression exp, Compilation comp, Target target)72     public static void compileUsingValues(Expression exp, Compilation comp,
73                                           Target target) {
74         ClassType typeValues = Compilation.typeValues;
75 	compileUsingConsumer(exp, comp, target,
76 			     typeValues.getDeclaredMethod("make", 0),
77 			     typeValues.getDeclaredMethod("canonicalize", 0));
78     }
79 
80     /** Compile an expression using a temporary Consumer, if needed. */
compileUsingConsumer(Expression exp, Compilation comp, Target target)81     public static void compileUsingConsumer(Expression exp, Compilation comp,
82                                             Target target) {
83         if (target instanceof IgnoreTarget || target instanceof ConsumerTarget)
84             exp.compile(comp, target);
85         else
86             compileUsingValues(exp, comp, target);
87     }
88 
compileUsingConsumer(Expression exp, Compilation comp, Target target, Method makeMethod, Method resultMethod)89   public static void compileUsingConsumer (Expression exp, Compilation comp,
90 					   Target target,
91 					   Method makeMethod,
92 					   Method resultMethod)
93   {
94     CodeAttr code = comp.getCode();
95     Scope scope = code.pushScope();
96     Type ctype;
97     if (makeMethod.getName() == "<init>")
98       {
99 	ClassType cltype = makeMethod.getDeclaringClass();
100 	ctype = cltype;
101 	code.emitNew(cltype);
102 	code.emitDup(ctype);
103 	code.emitInvoke(makeMethod);
104       }
105     else
106       {
107 	ctype = makeMethod.getReturnType();
108 	code.emitInvokeStatic(makeMethod);
109       }
110     Variable consumer = scope.addVariable(code, ctype, null);
111     ConsumerTarget ctarget = new ConsumerTarget(consumer, exp.getType());
112     code.emitStore(consumer);
113     exp.compile(comp, ctarget);
114     code.emitLoad(consumer);
115     if (resultMethod != null)
116       code.emitInvoke(resultMethod);
117     code.popScope();
118     target.compileFromStack(comp, exp.getType());
119   }
120 
121 
compileFromStack(Compilation comp, Type stackType)122   public void compileFromStack(Compilation comp, Type stackType)
123   {
124     compileFromStack(comp, stackType, -1);
125   }
126 
127   /** Write stack value to Consumer.
128    * @param consumerPushed if -1, then Consumer has not been pushed;
129    *   if 1, Consumer was pushed before value, and value is a known singleton;
130    *   if 0, Consumer was pushed before value, otherwise.
131    */
compileFromStack(Compilation comp, Type stackType, int consumerPushed)132   void compileFromStack(Compilation comp,
133                         Type stackType, int consumerPushed)
134   {
135     CodeAttr code = comp.getCode();
136     String methodName = null;
137     Method method = null;
138     ClassType methodClass = Compilation.typeConsumer;
139     Type methodArg = null;
140     boolean islong = false;
141     char sig;
142     Type ttype = getType();
143     if (! stackType.isVoid() && this != contextInstance) {
144         StackTarget.convert(comp, stackType, ttype);
145         stackType = ttype;
146     }
147     // We don't want to push a character as an int (which is its
148     // implementation type) since it isn't an integer.  So we box it.
149     if (stackType instanceof LangPrimType
150         && (stackType == LangPrimType.characterType ||
151             stackType == LangPrimType.characterOrEofType)) {
152         stackType.emitCoerceToObject(code);
153         stackType = Type.objectType;
154     }
155     if (consumerPushed < 0 && ! stackType.isVoid()) {
156         if (this == contextInstance) {
157             comp.loadCallContext();
158             code.emitGetField(Compilation.typeCallContext
159                               .getDeclaredField("consumer"));
160         }
161         else
162             code.emitLoad(consumer);
163     }
164     Type implType = stackType.getImplementationType();
165     if (implType instanceof PrimType)
166       {
167 	sig = implType.getSignature().charAt(0);
168 	switch (sig)
169 	  {
170           case 'I':
171               if (stackType == LangPrimType.unsignedIntType) {
172                   methodName = "writeUInt";
173                   methodClass = typeSequences;
174                   break;
175               }
176               // ... otherwise fall through ...
177 	  case 'B': case 'S':
178 	    methodName = "writeInt";
179             methodArg = Type.intType;
180             break;
181 	  case 'J':
182               if (stackType == LangPrimType.unsignedLongType) {
183                   methodName = "writeULong";
184                   methodClass = typeSequences;
185               } else {
186                   methodName = "writeLong";
187                   methodArg = Type.longType;
188               }
189             islong = true;
190             break;
191 	  case 'F':
192             methodName = "writeFloat";
193             methodArg = Type.floatType;
194             break;
195 	  case 'D':
196             methodName = "writeDouble";
197             methodArg = Type.doubleType;
198             islong = true;
199             break;
200 	  case 'C':
201             /* #ifdef JAVA5 */
202             methodName = "append";
203             methodArg = Type.charType;
204             /* #else */
205             // methodName = "write";
206             // methodArg = Type.intType;
207             /* #endif */
208             break;
209 	  case 'Z':
210             methodName = "writeBoolean";
211             methodArg = Type.booleanType;
212             break;
213 	  case 'V':
214             return;
215 	  }
216       }
217     else
218       {
219         sig = '\0';
220 	if (consumerPushed == 1 || OccurrenceType.itemCountIsOne(implType))
221           {
222             methodName = "writeObject";
223             methodArg = Type.pointer_type;
224           }
225 	else
226 	  {
227 	    method = (Compilation.typeValues
228 		      .getDeclaredMethod("writeValues", 2));
229             if (consumerPushed >= 0)
230               code.emitSwap();
231 	    code.emitInvokeStatic(method);
232 	    return;
233 	  }
234       }
235     if (consumerPushed >= 0)
236       {
237         if (methodClass == typeSequences) throw new InternalError();
238       }
239     else if (methodClass == typeSequences)
240       {
241 	code.emitLoad(consumer);
242       }
243     else if (islong)
244       {
245         code.emitDupX();  // dup_x2
246         code.emitPop(1);
247       }
248     else
249       code.emitSwap();
250     code.fixUnsigned(stackType);
251     if (methodClass == typeSequences)
252       {
253         method = methodClass.getDeclaredMethod(methodName, 2);
254       }
255     else if (method == null && methodName != null)
256       {
257         Type[] methodArgs = { methodArg };
258         method = methodClass.getDeclaredMethod(methodName, methodArgs);
259       }
260     if (method != null)
261       code.emitInvoke(method);
262     if (sig == 'C')
263       code.emitPop(1); // Pop consumer result.
264   }
265 
compileWrite(Expression exp, Compilation comp)266   public boolean compileWrite (Expression exp, Compilation comp)
267   {
268     Type stackType = exp.getType();
269     Type implType = stackType.getImplementationType();
270     if (implType instanceof PrimType
271         ? (! implType.isVoid()
272            && stackType != LangPrimType.characterType
273            && stackType != LangPrimType.characterOrEofType
274            && stackType != LangPrimType.unsignedLongType
275            && stackType != LangPrimType.unsignedIntType)
276         : gnu.kawa.reflect.OccurrenceType.itemCountIsOne(implType))
277       {
278         // Optimization to avoid a 'swap'.
279         comp.getCode().emitLoad(this.consumer);
280         Type ttype = this.type;
281         exp.compile(comp, StackTarget.getInstance(ttype));
282         compileFromStack(comp, ttype, 1);
283         return true;
284       }
285     return false;
286    }
287 
getType()288   public Type getType() { return type; }
289 
290     public static final ClassType typeSequences =
291       ClassType.make("gnu.lists.Sequences");
292 }
293