/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. */ package com.db4o.instrumentation.bloat; import java.io.*; import java.lang.reflect.*; import java.util.*; import EDU.purdue.cs.bloat.editor.*; import EDU.purdue.cs.bloat.editor.Type; import EDU.purdue.cs.bloat.reflect.*; import com.db4o.instrumentation.api.*; import com.db4o.instrumentation.util.*; class BloatMethodBuilder implements MethodBuilder { private final MethodEditor methodEditor; private final LabelGenerator _labelGen; private final BloatReferenceProvider _references; private final Map _conversions = new HashMap(); BloatMethodBuilder(BloatReferenceProvider references, ClassEditor classEditor, String methodName, TypeRef returnType, TypeRef[] parameterTypes) { _references = references; methodEditor = new MethodEditor(classEditor, Modifiers.PUBLIC, BloatTypeRef.bloatType(returnType), methodName, BloatTypeRef.bloatTypes(parameterTypes), new Type[]{}); _labelGen = new LabelGenerator(); methodEditor.addLabel(_labelGen.createLabel(true)); setUpConversions(); } public void invoke(final MethodRef method, CallingConvention convention) { if (convention == CallingConvention.INTERFACE) { invokeInterface(method); } else if (convention == CallingConvention.STATIC) { invokeStatic(method); } else { invokeVirtual(method); } } private void invokeInterface(final MethodRef method) { addInstruction(Opcode.opc_invokeinterface, memberRef(method)); } private void invokeVirtual(final MethodRef methodRef) { addInstruction(Opcode.opc_invokevirtual, memberRef(methodRef)); } private void invokeStatic(final MethodRef methodRef) { addInstruction(Opcode.opc_invokestatic, memberRef(methodRef)); } public void ldc(Object value) { addInstruction(Opcode.opc_ldc, coerce(value)); } public void loadArgument(final int index) { addInstruction(Opcode.opc_aload, new LocalVariable(index)); } public void pop() { addInstruction(Opcode.opc_pop); } private MemberRef memberRef(Object ref) { return ((BloatMemberRef)ref).member(); } public void endMethod() { addLabel(false); addInstruction(Opcode.opc_return); addLabel(true); methodEditor.commit(); } private void addLabel(final boolean startsBlock) { methodEditor.addLabel(_labelGen.createLabel(startsBlock)); } public void addInstruction(final int opcode) { methodEditor.addInstruction(opcode); } public void print(PrintStream out) { methodEditor.print(out); } public void loadArrayElement(TypeRef elementType) { addInstruction(arrayElementOpcode(elementType)); } private int arrayElementOpcode(TypeRef elementType) { if(elementType==integerType()) { return Opcode.opc_iaload; } if(elementType==longType()) { return Opcode.opc_laload; } if(elementType==floatType()) { return Opcode.opc_faload; } if(elementType==doubleType()) { return Opcode.opc_daload; } return Opcode.opc_aaload; } private TypeRef doubleType() { return type(Double.TYPE); } private TypeRef floatType() { return type(Float.TYPE); } private TypeRef longType() { return type(Long.TYPE); } private TypeRef integerType() { return type(Integer.TYPE); } private TypeRef type(Class type) { return _references.forType(type); } public void addInstruction(Instruction instruction) { methodEditor.addInstruction(instruction); } public void addInstruction(int opcode, Object operand) { methodEditor.addInstruction(opcode, operand); } public void add(TypeRef operandType) { addInstruction(addOpcode(operandType)); } private int addOpcode(TypeRef operandType) { if(operandType==doubleType()) { return Opcode.opc_dadd; } if(operandType==floatType()) { return Opcode.opc_fadd; } if(operandType==longType()) { return Opcode.opc_ladd; } return Opcode.opc_iadd; } public void subtract(TypeRef operandType) { addInstruction(subOpcode(operandType)); } private int subOpcode(TypeRef operandType) { if(operandType==doubleType()) { return Opcode.opc_dsub; } if(operandType==floatType()) { return Opcode.opc_fsub; } if(operandType==longType()) { return Opcode.opc_lsub; } return Opcode.opc_isub; } public void multiply(TypeRef operandType) { addInstruction(multOpcode(operandType)); } private int multOpcode(TypeRef operandType) { if(operandType==doubleType()) { return Opcode.opc_dmul; } if(operandType==floatType()) { return Opcode.opc_fmul; } if(operandType==longType()) { return Opcode.opc_lmul; } return Opcode.opc_imul; } public void divide(TypeRef operandType) { addInstruction(divOpcode(operandType)); } public void modulo(TypeRef operandType) { addInstruction(modOpcode(operandType)); } private int divOpcode(TypeRef operandType) { if(operandType==doubleType()) { return Opcode.opc_ddiv; } if(operandType==floatType()) { return Opcode.opc_fdiv; } if(operandType==longType()) { return Opcode.opc_ldiv; } return Opcode.opc_idiv; } private int modOpcode(TypeRef operandType) { if(operandType==doubleType()) { return Opcode.opc_drem; } if(operandType==floatType()) { return Opcode.opc_frem; } if(operandType==longType()) { return Opcode.opc_lrem; } return Opcode.opc_irem; } public void invoke(Method method) { final MethodRef methodRef = _references.forMethod(method); if (isStatic(method)) { invokeStatic(methodRef); } else { invokeVirtual(methodRef); } } private boolean isStatic(Method method) { return (method.getModifiers()&Modifier.STATIC)!=0; } public ReferenceProvider references() { return _references; } public void loadField(FieldRef fieldRef) { addInstruction(Opcode.opc_getfield, memberRef(fieldRef)); } public void loadStaticField(FieldRef fieldRef) { addInstruction(Opcode.opc_getstatic, memberRef(fieldRef)); } public void box(TypeRef boxedType) { Class[] convSpec=(Class[])_conversions.get(boxedType); if (null == convSpec) { return; } final Class wrapperType = convSpec[0]; final Class primitiveType = convSpec[1]; final LocalVariable local = methodEditor.newLocal(bloatType(primitiveType)); addInstruction(storeOpcode(primitiveType), local); addInstruction(Opcode.opc_new, bloatType(wrapperType)); addInstruction(Opcode.opc_dup); addInstruction(loadOpcode(primitiveType), local); addInstruction(Opcode.opc_invokespecial, memberRef(_references.forMethod(type(convSpec[0]),"<init>",new TypeRef[]{type(primitiveType)},type(Void.TYPE)))); } private int loadOpcode(Class type) { if(type==Long.TYPE) { return Opcode.opc_lload; } if(type==Float.TYPE) { return Opcode.opc_fload; } if(type==Double.TYPE) { return Opcode.opc_dload; } return Opcode.opc_iload; } private int storeOpcode(Class type) { if(type==Long.TYPE) { return Opcode.opc_lstore; } if(type==Float.TYPE) { return Opcode.opc_fstore; } if(type==Double.TYPE) { return Opcode.opc_dstore; } return Opcode.opc_istore; } private Type bloatType(final Class type) { return _references.bloatType(type); } private void setUpConversions() { setUpConversion(new Class[]{Integer.class,Integer.TYPE}); setUpConversion(new Class[]{Long.class,Long.TYPE}); setUpConversion(new Class[]{Short.class,Short.TYPE}); setUpConversion(new Class[]{Byte.class,Byte.TYPE}); setUpConversion(new Class[]{Double.class,Double.TYPE}); setUpConversion(new Class[]{Float.class,Float.TYPE}); setUpConversion(new Class[]{Boolean.class,Boolean.TYPE}); } private void setUpConversion(Class[] classes) { for (int i = 0; i < classes.length; i++) { _conversions.put(type(classes[i]), classes); } } private Object coerce(Object value) { if(value instanceof Boolean) { return ((Boolean)value).booleanValue() ? new Integer(1) : new Integer(0); } if(value instanceof Character) { return new Integer(((Character)value).charValue()); } if(value instanceof Byte || value instanceof Short) { return new Integer(((Number)value).intValue()); } return value; } }