001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.nativequery.optimization;
022:
023: import java.lang.reflect.*;
024: import java.lang.reflect.Method;
025: import java.util.*;
026:
027: import EDU.purdue.cs.bloat.editor.*;
028: import EDU.purdue.cs.bloat.editor.Type;
029: import EDU.purdue.cs.bloat.file.*;
030:
031: import com.db4o.nativequery.expr.cmp.*;
032: import com.db4o.nativequery.expr.cmp.operand.*;
033:
034: class ComparisonBytecodeGeneratingVisitor implements
035: ComparisonOperandVisitor {
036: private MethodEditor methodEditor;
037: private Class predicateClass;
038: private Class candidateClass;
039:
040: private Map conversions;
041: private boolean inArithmetic = false;
042: private Class opClass = null;
043: private Class staticRoot = null;
044: private ClassSource classSource;
045:
046: public ComparisonBytecodeGeneratingVisitor(
047: MethodEditor methodEditor, Class predicateClass,
048: Class candidateClass, ClassSource classSource) {
049: this .methodEditor = methodEditor;
050: this .predicateClass = predicateClass;
051: this .candidateClass = candidateClass;
052: this .classSource = classSource;
053: buildConversions();
054: }
055:
056: public void visit(ConstValue operand) {
057: Object value = operand.value();
058: if (value != null) {
059: opClass = value.getClass();
060: prepareConversion(value.getClass(), !inArithmetic);
061: }
062: methodEditor.addInstruction(Opcode.opc_ldc, coerce(value));
063: if (value != null) {
064: applyConversion(value.getClass(), !inArithmetic);
065: }
066: }
067:
068: private Object coerce(Object value) {
069: if (value instanceof Boolean) {
070: return ((Boolean) value).booleanValue() ? new Integer(1)
071: : new Integer(0);
072: }
073: if (value instanceof Character) {
074: return new Integer(((Character) value).charValue());
075: }
076: if (value instanceof Byte || value instanceof Short) {
077: return new Integer(((Number) value).intValue());
078: }
079: return value;
080: }
081:
082: public void visit(FieldValue fieldValue) {
083: try {
084: Class lastFieldClass = deduceFieldClass(fieldValue);
085: Class parentClass = deduceFieldClass(fieldValue.parent());
086: boolean needConversion = lastFieldClass.isPrimitive();
087: prepareConversion(lastFieldClass, !inArithmetic
088: && needConversion);
089:
090: fieldValue.parent().accept(this );
091: if (staticRoot != null) {
092: methodEditor
093: .addInstruction(Opcode.opc_getstatic,
094: createFieldReference(staticRoot,
095: lastFieldClass, fieldValue
096: .fieldName()));
097: staticRoot = null;
098: return;
099: }
100: MemberRef fieldRef = createFieldReference(parentClass,
101: lastFieldClass, fieldValue.fieldName());
102: methodEditor.addInstruction(Opcode.opc_getfield, fieldRef);
103:
104: applyConversion(lastFieldClass, !inArithmetic
105: && needConversion);
106: } catch (Exception exc) {
107: throw new RuntimeException(exc.getMessage());
108: }
109: }
110:
111: public void visit(CandidateFieldRoot root) {
112: methodEditor.addInstruction(Opcode.opc_aload,
113: new LocalVariable(1));
114: }
115:
116: public void visit(PredicateFieldRoot root) {
117: methodEditor.addInstruction(Opcode.opc_aload,
118: new LocalVariable(0));
119: }
120:
121: public void visit(StaticFieldRoot root) {
122: try {
123: staticRoot = classSource.loadClass((root.className()));
124: } catch (ClassNotFoundException e) {
125: e.printStackTrace();
126: }
127: }
128:
129: public void visit(ArrayAccessValue operand) {
130: Class cmpType = deduceFieldClass(operand.parent())
131: .getComponentType();
132: prepareConversion(cmpType, !inArithmetic);
133: operand.parent().accept(this );
134: boolean outerInArithmetic = inArithmetic;
135: inArithmetic = true;
136: operand.index().accept(this );
137: inArithmetic = outerInArithmetic;
138: int opcode = Opcode.opc_aaload;
139: if (cmpType == Integer.TYPE) {
140: opcode = Opcode.opc_iaload;
141: }
142: if (cmpType == Long.TYPE) {
143: opcode = Opcode.opc_laload;
144: }
145: if (cmpType == Float.TYPE) {
146: opcode = Opcode.opc_faload;
147: }
148: if (cmpType == Double.TYPE) {
149: opcode = Opcode.opc_daload;
150: }
151: methodEditor.addInstruction(opcode);
152: applyConversion(cmpType, !inArithmetic);
153: }
154:
155: public void visit(MethodCallValue operand) {
156: Class rcvType = deduceFieldClass(operand.parent());
157: Method method = ReflectUtil.methodFor(rcvType, operand
158: .methodName(), operand.paramTypes());
159: Class retType = method.getReturnType();
160: // FIXME: this should be handled within conversions
161: boolean needConversion = retType.isPrimitive();
162: prepareConversion(retType, !inArithmetic && needConversion);
163: operand.parent().accept(this );
164: boolean oldInArithmetic = inArithmetic;
165: for (int paramIdx = 0; paramIdx < operand.args().length; paramIdx++) {
166: inArithmetic = operand.paramTypes()[paramIdx].isPrimitive();
167: operand.args()[paramIdx].accept(this );
168: }
169: inArithmetic = oldInArithmetic;
170: // FIXME: invokeinterface
171: int opcode = ((method.getModifiers() & Modifier.STATIC) != 0 ? Opcode.opc_invokestatic
172: : Opcode.opc_invokevirtual);
173: methodEditor.addInstruction(opcode, createMethodReference(
174: method.getDeclaringClass(), method.getName(), method
175: .getParameterTypes(), method.getReturnType()));
176: applyConversion(retType, !inArithmetic && needConversion);
177: }
178:
179: public void visit(ArithmeticExpression operand) {
180: boolean oldInArithmetic = inArithmetic;
181: inArithmetic = true;
182: Instruction newInstr = prepareConversion(opClass,
183: !oldInArithmetic, true);
184: operand.left().accept(this );
185: operand.right().accept(this );
186: Class operandType = arithmeticType(operand);
187: int opcode = Integer.MIN_VALUE;
188: switch (operand.op().id()) {
189: case ArithmeticOperator.ADD_ID:
190: if (operandType == Double.class) {
191: opcode = Opcode.opc_dadd;
192: break;
193: }
194: if (operandType == Float.class) {
195: opcode = Opcode.opc_fadd;
196: break;
197: }
198: if (operandType == Long.class) {
199: opcode = Opcode.opc_ladd;
200: break;
201: }
202: opcode = Opcode.opc_iadd;
203: break;
204: case ArithmeticOperator.SUBTRACT_ID:
205: if (operandType == Double.class) {
206: opcode = Opcode.opc_dsub;
207: break;
208: }
209: if (operandType == Float.class) {
210: opcode = Opcode.opc_fsub;
211: break;
212: }
213: if (operandType == Long.class) {
214: opcode = Opcode.opc_lsub;
215: break;
216: }
217: opcode = Opcode.opc_isub;
218: break;
219: case ArithmeticOperator.MULTIPLY_ID:
220: if (operandType == Double.class) {
221: opcode = Opcode.opc_dmul;
222: break;
223: }
224: if (operandType == Float.class) {
225: opcode = Opcode.opc_fmul;
226: break;
227: }
228: if (operandType == Long.class) {
229: opcode = Opcode.opc_lmul;
230: break;
231: }
232: opcode = Opcode.opc_imul;
233: break;
234: case ArithmeticOperator.DIVIDE_ID:
235: if (operandType == Double.class) {
236: opcode = Opcode.opc_ddiv;
237: break;
238: }
239: if (operandType == Float.class) {
240: opcode = Opcode.opc_fdiv;
241: break;
242: }
243: if (operandType == Long.class) {
244: opcode = Opcode.opc_ldiv;
245: break;
246: }
247: opcode = Opcode.opc_idiv;
248: break;
249: default:
250: throw new RuntimeException("Unknown operand: "
251: + operand.op());
252: }
253: methodEditor.addInstruction(opcode);
254: if (newInstr != null) {
255: newInstr.setOperand(createType(opClass));
256: }
257: applyConversion(opClass, !oldInArithmetic);
258: inArithmetic = oldInArithmetic;
259: // FIXME: need to map dX,fX,...
260: }
261:
262: private Class deduceFieldClass(ComparisonOperand fieldValue) {
263: TypeDeducingVisitor visitor = new TypeDeducingVisitor(
264: predicateClass, candidateClass, classSource);
265: fieldValue.accept(visitor);
266: return visitor.operandClass();
267: }
268:
269: private MemberRef createFieldReference(Class parentClass,
270: Class fieldClass, String name) throws NoSuchFieldException {
271: NameAndType nameAndType = new NameAndType(name,
272: createType(fieldClass));
273: return new MemberRef(createType(parentClass), nameAndType);
274: }
275:
276: private Class arithmeticType(ComparisonOperand operand) {
277: if (operand instanceof ConstValue) {
278: return ((ConstValue) operand).value().getClass();
279: }
280: if (operand instanceof FieldValue) {
281: try {
282: return deduceFieldClass(operand);
283: } catch (Exception e) {
284: e.printStackTrace();
285: return null;
286: }
287: }
288: if (operand instanceof ArithmeticExpression) {
289: ArithmeticExpression expr = (ArithmeticExpression) operand;
290: Class left = arithmeticType(expr.left());
291: Class right = arithmeticType(expr.right());
292: if (left == Double.class || right == Double.class) {
293: return Double.class;
294: }
295: if (left == Float.class || right == Float.class) {
296: return Float.class;
297: }
298: if (left == Long.class || right == Long.class) {
299: return Long.class;
300: }
301: return Integer.class;
302: }
303: return null;
304: }
305:
306: private Instruction prepareConversion(Class clazz, boolean canApply) {
307: return prepareConversion(clazz, canApply, false);
308: }
309:
310: private Instruction prepareConversion(Class clazz,
311: boolean canApply, boolean force) {
312: if ((force || conversions.containsKey(clazz)) && canApply) {
313: Class[] convSpec = (Class[]) conversions.get(clazz);
314: Instruction newInstruction = new Instruction(
315: Opcode.opc_new, (convSpec == null ? null
316: : createType(convSpec[0])));
317: methodEditor.addInstruction(newInstruction);
318: methodEditor.addInstruction(Opcode.opc_dup);
319: return newInstruction;
320: }
321: return null;
322: }
323:
324: private void applyConversion(Class clazz, boolean canApply) {
325: if (conversions.containsKey(clazz) && canApply) {
326: Class[] convSpec = (Class[]) conversions.get(clazz);
327: methodEditor.addInstruction(Opcode.opc_invokespecial,
328: createMethodReference(convSpec[0], "<init>",
329: new Class[] { convSpec[1] }, Void.TYPE));
330: }
331: }
332:
333: private MemberRef createMethodReference(Class parent, String name,
334: Class[] args, Class ret) {
335: Type[] argTypes = new Type[args.length];
336: for (int argIdx = 0; argIdx < args.length; argIdx++) {
337: argTypes[argIdx] = createType(args[argIdx]);
338: }
339: NameAndType nameAndType = new NameAndType(name, Type.getType(
340: argTypes, createType(ret)));
341: return new MemberRef(createType(parent), nameAndType);
342: }
343:
344: private Type createType(Class clazz) {
345: return Type.getType(clazz);
346: }
347:
348: private void buildConversions() {
349: conversions = new HashMap();
350: conversions.put(Integer.class, new Class[] { Integer.class,
351: Integer.TYPE });
352: conversions.put(Long.class,
353: new Class[] { Long.class, Long.TYPE });
354: conversions.put(Short.class, new Class[] { Short.class,
355: Short.TYPE });
356: conversions.put(Byte.class,
357: new Class[] { Byte.class, Byte.TYPE });
358: conversions.put(Double.class, new Class[] { Double.class,
359: Double.TYPE });
360: conversions.put(Float.class, new Class[] { Float.class,
361: Float.TYPE });
362: conversions.put(Boolean.class, new Class[] { Boolean.class,
363: Boolean.TYPE });
364: // FIXME this must be handled somewhere else - FieldValue, etc.
365: conversions.put(Integer.TYPE, conversions.get(Integer.class));
366: conversions.put(Long.TYPE, conversions.get(Long.class));
367: conversions.put(Short.TYPE, conversions.get(Short.class));
368: conversions.put(Byte.TYPE, conversions.get(Byte.class));
369: conversions.put(Double.TYPE, conversions.get(Double.class));
370: conversions.put(Float.TYPE, conversions.get(Float.class));
371: conversions.put(Boolean.TYPE, conversions.get(Boolean.class));
372: }
373: }
|