1 21 package com.db4o.nativequery.optimization; 22 23 import java.lang.reflect.*; 24 import java.util.*; 25 26 import EDU.purdue.cs.bloat.editor.*; 27 import EDU.purdue.cs.bloat.editor.Type; 28 29 import com.db4o.nativequery.expr.cmp.*; 30 import com.db4o.nativequery.expr.cmp.field.*; 31 32 class ComparisonBytecodeGeneratingVisitor implements ComparisonOperandVisitor { 33 private MethodEditor methodEditor; 34 private Class predicateClass; 35 private Class candidateClass; 36 37 private Map conversions; 38 private boolean inArithmetic=false; 39 private Class opClass=null; 40 private Class staticRoot=null; 41 42 public ComparisonBytecodeGeneratingVisitor(MethodEditor methodEditor,Class predicateClass,Class candidateClass) { 43 this.methodEditor = methodEditor; 44 this.predicateClass=predicateClass; 45 this.candidateClass=candidateClass; 46 buildConversions(); 47 } 48 49 public void visit(ConstValue operand) { 50 Object value = operand.value(); 51 if(value!=null) { 52 opClass=value.getClass(); 53 prepareConversion(value.getClass(),!inArithmetic); 54 } 55 methodEditor.addInstruction(Opcode.opc_ldc,value); 56 if(value!=null) { 57 applyConversion(value.getClass(),!inArithmetic); 58 } 59 } 61 62 public void visit(FieldValue fieldValue) { 63 try { 64 Class lastFieldClass = deduceFieldClass(fieldValue); 65 Class parentClass=deduceFieldClass(fieldValue.parent()); 66 boolean needConversion=lastFieldClass.isPrimitive(); 67 prepareConversion(lastFieldClass,!inArithmetic&&needConversion); 68 69 fieldValue.parent().accept(this); 70 if(staticRoot!=null) { 71 methodEditor.addInstruction(Opcode.opc_getstatic,createFieldReference(staticRoot, lastFieldClass,fieldValue.fieldName())); 72 staticRoot=null; 73 return; 74 } 75 MemberRef fieldRef=createFieldReference(parentClass,lastFieldClass,fieldValue.fieldName()); 76 methodEditor.addInstruction(Opcode.opc_getfield,fieldRef); 77 78 applyConversion(lastFieldClass,!inArithmetic&&needConversion); 79 } catch (Exception exc) { 80 throw new RuntimeException (exc.getMessage()); 81 } 82 } 83 84 public void visit(CandidateFieldRoot root) { 85 methodEditor.addInstruction(Opcode.opc_aload,new LocalVariable(1)); 86 } 87 88 public void visit(PredicateFieldRoot root) { 89 methodEditor.addInstruction(Opcode.opc_aload,new LocalVariable(0)); 90 } 91 92 public void visit(StaticFieldRoot root) { 93 try { 94 staticRoot=Class.forName(root.className()); 95 } catch (ClassNotFoundException e) { 96 e.printStackTrace(); 97 } 98 } 99 100 public void visit(ArrayAccessValue operand) { 101 Class cmpType=deduceFieldClass(operand.parent()).getComponentType(); 102 prepareConversion(cmpType, !inArithmetic); 103 operand.parent().accept(this); 104 boolean outerInArithmetic=inArithmetic; 105 inArithmetic=true; 106 operand.index().accept(this); 107 inArithmetic=outerInArithmetic; 108 int opcode=Opcode.opc_aaload; 109 if(cmpType==Integer.TYPE) { 110 opcode=Opcode.opc_iaload; 111 } 112 if(cmpType==Long.TYPE) { 113 opcode=Opcode.opc_laload; 114 } 115 if(cmpType==Float.TYPE) { 116 opcode=Opcode.opc_faload; 117 } 118 if(cmpType==Double.TYPE) { 119 opcode=Opcode.opc_daload; 120 } 121 methodEditor.addInstruction(opcode); 122 applyConversion(cmpType, !inArithmetic); 123 } 124 125 public void visit(MethodCallValue operand) { 126 Class rcvType=deduceFieldClass(operand.parent()); 127 Method method=ReflectUtil.methodFor(rcvType, operand.methodName(), operand.paramTypes()); 128 Class retType=method.getReturnType(); 129 boolean needConversion=retType.isPrimitive(); 131 prepareConversion(retType, !inArithmetic&&needConversion); 132 operand.parent().accept(this); 133 boolean oldInArithmetic=inArithmetic; 134 for (int paramIdx = 0; paramIdx < operand.args().length; paramIdx++) { 135 inArithmetic=operand.paramTypes()[paramIdx].isPrimitive(); 136 operand.args()[paramIdx].accept(this); 137 } 138 inArithmetic=oldInArithmetic; 139 int opcode=((method.getModifiers()&Modifier.STATIC)!=0 ? Opcode.opc_invokestatic : Opcode.opc_invokevirtual); 141 methodEditor.addInstruction(opcode,createMethodReference(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType())); 142 applyConversion(retType, !inArithmetic&&needConversion); 143 } 144 145 public void visit(ArithmeticExpression operand) { 146 boolean oldInArithmetic=inArithmetic; 147 inArithmetic=true; 148 Instruction newInstr=prepareConversion(opClass,!oldInArithmetic,true); 149 operand.left().accept(this); 150 operand.right().accept(this); 151 Class operandType=arithmeticType(operand); 152 int opcode=Integer.MIN_VALUE; 153 switch(operand.op().id()) { 154 case ArithmeticOperator.ADD_ID: 155 if(operandType==Double .class) { 156 opcode=Opcode.opc_dadd; 157 break; 158 } 159 if(operandType==Float .class) { 160 opcode=Opcode.opc_fadd; 161 break; 162 } 163 if(operandType==Long .class) { 164 opcode=Opcode.opc_ladd; 165 break; 166 } 167 opcode=Opcode.opc_iadd; 168 break; 169 case ArithmeticOperator.SUBTRACT_ID: 170 if(operandType==Double .class) { 171 opcode=Opcode.opc_dsub; 172 break; 173 } 174 if(operandType==Float .class) { 175 opcode=Opcode.opc_fsub; 176 break; 177 } 178 if(operandType==Long .class) { 179 opcode=Opcode.opc_lsub; 180 break; 181 } 182 opcode=Opcode.opc_isub; 183 break; 184 case ArithmeticOperator.MULTIPLY_ID: 185 if(operandType==Double .class) { 186 opcode=Opcode.opc_dmul; 187 break; 188 } 189 if(operandType==Float .class) { 190 opcode=Opcode.opc_fmul; 191 break; 192 } 193 if(operandType==Long .class) { 194 opcode=Opcode.opc_lmul; 195 break; 196 } 197 opcode=Opcode.opc_imul; 198 break; 199 case ArithmeticOperator.DIVIDE_ID: 200 if(operandType==Double .class) { 201 opcode=Opcode.opc_ddiv; 202 break; 203 } 204 if(operandType==Float .class) { 205 opcode=Opcode.opc_fdiv; 206 break; 207 } 208 if(operandType==Long .class) { 209 opcode=Opcode.opc_ldiv; 210 break; 211 } 212 opcode=Opcode.opc_idiv; 213 break; 214 default: 215 throw new RuntimeException ("Unknown operand: "+operand.op()); 216 } 217 methodEditor.addInstruction(opcode); 218 if(newInstr!=null) { 219 newInstr.setOperand(createType(opClass)); 220 } 221 applyConversion(opClass,!oldInArithmetic); 222 inArithmetic=oldInArithmetic; 223 } 225 226 private Class deduceFieldClass(ComparisonOperand fieldValue) { 227 TypeDeducingVisitor visitor=new TypeDeducingVisitor(predicateClass,candidateClass); 228 fieldValue.accept(visitor); 229 return visitor.operandClass(); 230 } 231 232 private MemberRef createFieldReference(Class parentClass,Class fieldClass,String name) throws NoSuchFieldException { 233 NameAndType nameAndType=new NameAndType(name,createType(fieldClass)); 234 return new MemberRef(createType(parentClass),nameAndType); 235 } 236 237 238 private Class arithmeticType(ComparisonOperand operand) { 239 if (operand instanceof ConstValue) { 240 return ((ConstValue) operand).value().getClass(); 241 } 242 if (operand instanceof FieldValue) { 243 try { 244 return deduceFieldClass((FieldValue) operand); 245 } catch (Exception e) { 246 e.printStackTrace(); 247 return null; 248 } 249 } 250 if (operand instanceof ArithmeticExpression) { 251 ArithmeticExpression expr=(ArithmeticExpression)operand; 252 Class left=arithmeticType(expr.left()); 253 Class right=arithmeticType(expr.right()); 254 if(left==Double .class||right==Double .class) { 255 return Double .class; 256 } 257 if(left==Float .class||right==Float .class) { 258 return Float .class; 259 } 260 if(left==Long .class||right==Long .class) { 261 return Long .class; 262 } 263 return Integer .class; 264 } 265 return null; 266 } 267 268 private Instruction prepareConversion(Class clazz,boolean canApply) { 269 return prepareConversion(clazz,canApply,false); 270 } 271 272 private Instruction prepareConversion(Class clazz,boolean canApply,boolean force) { 273 if((force||conversions.containsKey(clazz))&&canApply) { 274 Class [] convSpec=(Class [])conversions.get(clazz); 275 Instruction newInstruction=new Instruction(Opcode.opc_new,(convSpec==null ? null : createType(convSpec[0]))); 276 methodEditor.addInstruction(newInstruction); 277 methodEditor.addInstruction(Opcode.opc_dup); 278 return newInstruction; 279 } 280 return null; 281 } 282 283 private void applyConversion(Class clazz,boolean canApply) { 284 if(conversions.containsKey(clazz)&&canApply) { 285 Class [] convSpec=(Class [])conversions.get(clazz); 286 methodEditor.addInstruction(Opcode.opc_invokespecial,createMethodReference(convSpec[0],"<init>",new Class []{convSpec[1]},Void.TYPE)); 287 } 288 } 289 290 private MemberRef createMethodReference(Class parent,String name,Class [] args,Class ret) { 291 Type[] argTypes=new Type[args.length]; 292 for (int argIdx = 0; argIdx < args.length; argIdx++) { 293 argTypes[argIdx]=createType(args[argIdx]); 294 } 295 NameAndType nameAndType=new NameAndType(name,Type.getType(argTypes,createType(ret))); 296 return new MemberRef(createType(parent),nameAndType); 297 } 298 299 private Type createType(Class clazz) { 300 return Type.getType(clazz); 301 } 302 303 private void buildConversions() { 304 conversions=new HashMap(); 305 conversions.put(Integer .class,new Class []{Integer .class,Integer.TYPE}); 306 conversions.put(Long .class,new Class []{Long .class,Long.TYPE}); 307 conversions.put(Short .class,new Class []{Short .class,Short.TYPE}); 308 conversions.put(Byte .class,new Class []{Byte .class,Byte.TYPE}); 309 conversions.put(Double .class,new Class []{Double .class,Double.TYPE}); 310 conversions.put(Float .class,new Class []{Float .class,Float.TYPE}); 311 conversions.put(Integer.TYPE,conversions.get(Integer .class)); 313 conversions.put(Long.TYPE,conversions.get(Long .class)); 314 conversions.put(Short.TYPE,conversions.get(Short .class)); 315 conversions.put(Byte.TYPE,conversions.get(Byte .class)); 316 conversions.put(Double.TYPE,conversions.get(Double .class)); 317 conversions.put(Float.TYPE,conversions.get(Float .class)); 318 } 319 } 320 | Popular Tags |