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