Java tutorial
/* * Copyright (C) 2013 uebb.tu-berlin.de. * * This file is part of JBOP (Java Bytecode OPtimizer). * * JBOP is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JBOP 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 Lesser General License for more details. * * You should have received a copy of the GNU Lesser General License * along with JBOP. If not, see <http://www.gnu.org/licenses/>. */ package de.tuberlin.uebb.jbop.optimizer.methodsplitter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.CodeSizeEvaluator; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.IincInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MultiANewArrayInsnNode; import org.objectweb.asm.tree.TypeInsnNode; import org.objectweb.asm.tree.VarInsnNode; import de.tuberlin.uebb.jbop.optimizer.loop.SplitMarkNode; import de.tuberlin.uebb.jbop.optimizer.utils.NodeHelper; /** * The Class Block. * * This is a DTO / BO that holds instructions for a Method. * Whenever a instruction is added to the block, parameters of the method * are updated on the fly. * * @author Christopher Ewest */ class Block { private final VarList readers = new VarList(); private final VarList writers = new VarList(); private final VarList parameters = new VarList(); private final Type[] args; /** The insns. */ private final List<AbstractInsnNode> insns = new ArrayList<>(); private CodeSizeEvaluator sizeEvaluator = new CodeSizeEvaluator(null); /** The num. */ private final int num; private final Map<Integer, Integer> varMap = new HashMap<Integer, Integer>(); private final InsnList pushParameters = new InsnList(); private int varIndex = 1; private final int firstLocal; private final int parameterIndexes[]; private final StringBuilder descBuilder = new StringBuilder("("); private final Type returnType; /** * Instantiates a new {@link Block}. * * @param num * the number of the block * @param args * the top-Level parameters of the method * @param returnType * the return type */ Block(final int num, final Type[] args, final Type returnType) { this.args = args; this.num = num; this.returnType = returnType; int i = 0; parameterIndexes = new int[args.length]; for (final Type type : args) { parameterIndexes[i] = varIndex; pushParameters.add(new VarInsnNode(getOpcode(type), varIndex)); parameters.add(new Var(varIndex, -1, VarType.READ, type)); varMap.put(Integer.valueOf(varIndex), Integer.valueOf(varIndex)); varIndex += type.getSize(); i++; descBuilder.append(type.getDescriptor()); } firstLocal = varIndex; } private int getOpcode(final Type type) { if (Type.BOOLEAN_TYPE.equals(type)) { return Opcodes.ILOAD; } if (Type.INT_TYPE.equals(type)) { return Opcodes.ILOAD; } if (Type.FLOAT_TYPE.equals(type)) { return Opcodes.FLOAD; } if (Type.LONG_TYPE.equals(type)) { return Opcodes.LLOAD; } if (Type.DOUBLE_TYPE.equals(type)) { return Opcodes.DLOAD; } if (Type.CHAR_TYPE.equals(type)) { return Opcodes.ILOAD; } return Opcodes.ALOAD; } /** * Gets the current Size of the method. * * @return the size * @see CodeSizeEvaluator */ int getSize() { return sizeEvaluator.getMaxSize(); } /** * To string. * * @return the string */ @Override public String toString() { return "Block " + num + ": " + getSize() + "bytes, " + getDescriptor(); } /** * Gets the descriptor. * * @return the descriptor */ String getDescriptor() { return descBuilder.toString() + ")" + returnType.getDescriptor(); } /** * Adds the insn to this block. * * @param insn * the insn */ void addInsn(final AbstractInsnNode insn) { addInsn(insn, false); } /** * Adds the insn to this block. * Computes parameters and types if deep is true. * * @param insn * the insn * @param deep * compute parameters and types? */ void addInsn(final AbstractInsnNode insn, final boolean deep) { insns.add(insn); if (!(insn instanceof SplitMarkNode)) { insn.accept(sizeEvaluator); } if (!deep) { return; } if (isStore(insn)) { final int index = getIndex(insn); if (writers.containsIndex(index)) { return; } final Type findType = findType(insn); if (insn instanceof InsnNode) { writers.add(new Var(-1, insns.indexOf(insn), VarType.WRITE, findType)); } else { final VarInsnNode var = (VarInsnNode) insn; writers.add(new Var(var.var, insns.indexOf(insn), VarType.WRITE, findType)); } } else if (isLoad(insn)) { final VarInsnNode var = (VarInsnNode) insn; final int index = var.var; if (index == 0) { return; } final Type findType = findType(insn); final Var e = new Var(index, insns.indexOf(insn), VarType.READ, findType); readers.add(e); if (!writers.containsIndex(index) && !parameters.containsIndex(index)) { parameters.add(e); final VarInsnNode node = new VarInsnNode(var.getOpcode(), index); pushParameters.add(node); descBuilder.append(findType.getDescriptor()); addVarMapping(var, findType); } } } private void addVarMapping(final VarInsnNode var, final Type type) { final Integer index = Integer.valueOf(var.var); if (varMap.containsKey(index)) { return; } varMap.put(index, Integer.valueOf(varIndex)); varIndex += type.getSize(); } /** * Gets the parameter types (including top-level parameters). * * @return the parameter types */ Type[] getParameterTypes() { final Type[] types = new Type[parameters.size()]; int i = 0; for (final Var parameter : parameters) { types[i++] = parameter.getParameterType(); } return types; } /** * Gets the push parameters. * * This is an instruction list preparing the stack to call *this* method. * * @return the push parameters */ InsnList getPushParameters() { return pushParameters; } /** * Gets the end type. * * @return the end type */ Type getEndType() { final AbstractInsnNode lastStore = getLastStore(); final int index = getIndex(lastStore); if (index == -1) { return Type.VOID_TYPE; } final Var lastVar = writers.getLastVar(index); if ((lastVar == null)) { if ((lastStore == null)) { return Type.VOID_TYPE; } return findType(lastStore); } return lastVar.getParameterType(); } /** * Finds the type of the given node. * * @param node * the node * @return the type */ Type findType(final AbstractInsnNode node) { final int index = getIndex(node); Type type = getTypeFromParam(index); if (type != null) { return type; } type = getTypeIfSimpleType(node); if (type != null) { return type; } // ALOAD type = getAlreadyKnownType(node, index); if (type != null) { return type; } return resolveType(node); } /** * Objects that are not parameters and not written before * these can be: * getField * getStatic * new * new array * new multi array * return type of method call */ private Type resolveType(final AbstractInsnNode node) { int arrayCount = 0; AbstractInsnNode currentNode = NodeHelper.getPrevious(node); while (currentNode != null) { final int opcode2 = currentNode.getOpcode(); if (opcode2 == Opcodes.NEWARRAY) { final int operand = ((IntInsnNode) currentNode).operand; return getObjectType(operand); } else if (opcode2 == Opcodes.ANEWARRAY) { return getObjectType(((TypeInsnNode) currentNode).desc); } else if (opcode2 == Opcodes.MULTIANEWARRAY) { return getObjectType(((MultiANewArrayInsnNode) currentNode).desc); } else if (opcode2 == Opcodes.NEW) { final String desc = ((TypeInsnNode) currentNode).desc; return getObjectType(desc); } else if ((opcode2 >= Opcodes.IALOAD) && (opcode2 <= Opcodes.AALOAD)) { arrayCount++; } else if ((opcode2 == Opcodes.GETFIELD) || (opcode2 == Opcodes.GETSTATIC)) { final String desc = ((FieldInsnNode) currentNode).desc; return getObjectType(removeArrayType(desc, arrayCount)); } else if ((opcode2 == Opcodes.ALOAD)) { final Type type2 = readers.getFirstVar(((VarInsnNode) currentNode).var).getParameterType(); return getObjectType(removeArrayType(type2.getDescriptor(), arrayCount)); } else if ((opcode2 >= Opcodes.INVOKEVIRTUAL) && (opcode2 <= Opcodes.INVOKEDYNAMIC)) { return Type.getReturnType(((MethodInsnNode) currentNode).desc); } currentNode = NodeHelper.getPrevious(currentNode); } return Type.VOID_TYPE; } /** * Objects that are loaded and are not parameters must have been written before */ private Type getAlreadyKnownType(final AbstractInsnNode node, final int index) { if (isLoad(node)) { final Var firstVar = writers.getFirstVar(index); if (firstVar == null) { // write was in a different block, so this is a new parameter for this method // so find the writer and return the type of it. return findType(getWriter(node)); } return firstVar.getParameterType(); } return null; } private Type getTypeIfSimpleType(final AbstractInsnNode node) { final int opcode = node.getOpcode(); if ((opcode == Opcodes.ILOAD) || (opcode == Opcodes.ISTORE) || (opcode == Opcodes.IRETURN)) { return Type.INT_TYPE; } if ((opcode == Opcodes.FLOAD) || (opcode == Opcodes.FSTORE) || (opcode == Opcodes.FRETURN)) { return Type.FLOAT_TYPE; } if ((opcode == Opcodes.LLOAD) || (opcode == Opcodes.LSTORE) || (opcode == Opcodes.LRETURN)) { return Type.LONG_TYPE; } if ((opcode == Opcodes.DLOAD) || (opcode == Opcodes.DSTORE) || (opcode == Opcodes.DRETURN)) { return Type.DOUBLE_TYPE; } return null; } /** * Types from methodParameters via methodDescriptor */ private Type getTypeFromParam(final int index) { if ((index > 0) && (index < firstLocal)) { int argsIndex = 1; for (final Type type : args) { if (index == argsIndex) { return type; } argsIndex += type.getSize(); } } return null; } private AbstractInsnNode getWriter(final AbstractInsnNode node) { final int index = getIndex(node); AbstractInsnNode currentNode = NodeHelper.getPrevious(node); while (currentNode != null) { currentNode = NodeHelper.getPrevious(currentNode); if (isStore(currentNode) && (getIndex(currentNode) == index)) { return currentNode; } } return null; } private String removeArrayType(final String desc, final int count) { String s = desc; for (int i = 0; i < count; ++i) { if (s.charAt(0) == '[') { s = s.substring(1); } } return s; } private Type getObjectType(final int operand) { if (Opcodes.T_INT == operand) { return Type.getType(int[].class); } if (Opcodes.T_FLOAT == operand) { return Type.getType(float[].class); } if (Opcodes.T_LONG == operand) { return Type.getType(long[].class); } if (Opcodes.T_DOUBLE == operand) { return Type.getType(double[].class); } return Type.getType(Object.class); } private Type getObjectType(final String operand) { return Type.getType(operand); } /** * Gets the writers. * * @return the writers */ VarList getWriters() { return writers; } private static boolean isLoad(final AbstractInsnNode currentNode) { if (currentNode == null) { return false; } if ((currentNode.getOpcode() >= Opcodes.ILOAD) && (currentNode.getOpcode() <= Opcodes.ALOAD)) { return true; } return false; } private static boolean isStore(final AbstractInsnNode currentNode) { if (currentNode == null) { return false; } if ((currentNode.getOpcode() >= Opcodes.ISTORE) && (currentNode.getOpcode() <= Opcodes.ASTORE)) { return true; } if ((currentNode.getOpcode() >= Opcodes.IRETURN) && (currentNode.getOpcode() <= Opcodes.RETURN)) { return true; } return false; } /** * Adds the block to this block. * Thereby the parameters and types are calculated. * * @param otherBlock * the other block */ void add(final Block otherBlock) { for (final AbstractInsnNode node : otherBlock.insns) { addInsn(node, true); } } /** * Clears this block. */ void clear() { readers.clear(); writers.clear(); parameters.clear(); insns.clear(); varMap.clear(); pushParameters.clear(); sizeEvaluator = new CodeSizeEvaluator(null); } /** * Rename insns. * This means: correct the indexes of the local variables to match the parameters. * * @param paramRenameMap * the param rename map */ void renameInsns(final Map<Integer, Integer> paramRenameMap) { for (final AbstractInsnNode node : insns) { renameIfVar(node, varMap); } for (final Iterator<AbstractInsnNode> it = pushParameters.iterator(); it.hasNext();) { renameIfVar(it.next(), paramRenameMap); } } private void renameIfVar(final AbstractInsnNode currentNode, final Map<Integer, Integer> varRenameMap) { final int index = getIndex(currentNode); if (index < firstLocal) { return; } final Integer key = Integer.valueOf(index); Integer value = varRenameMap.get(key); if (value == null) { value = Integer.valueOf(varIndex); varIndex += writers.getFirstVar(index).getParameterType().getSize(); varRenameMap.put(key, value); } final int mapped = value.intValue(); setIndex(currentNode, mapped); } private int getIndex(final AbstractInsnNode node) { if (node instanceof VarInsnNode) { return ((VarInsnNode) node).var; } else if (node instanceof IincInsnNode) { return ((IincInsnNode) node).var; } else { return -1; } } private void setIndex(final AbstractInsnNode node, final int index) { if (node instanceof VarInsnNode) { ((VarInsnNode) node).var = index; } else if (node instanceof IincInsnNode) { ((IincInsnNode) node).var = index; } } /** * Gets the last store. * * @return the last store or null if no store occurs */ AbstractInsnNode getLastStore() { for (int i = insns.size() - 1; i >= 0; --i) { final AbstractInsnNode insn = insns.get(i); if (isStore(insn)) { return insn; } } return null; } /** * Gets the block number. * * @return the block number */ int getBlockNumber() { return num; } /** * Gets the instructions. * * @return the instructions */ List<AbstractInsnNode> getInstructions() { return Collections.unmodifiableList(insns); } /** * Gets the parameters. * * @return the parameters */ VarList getParameters() { return parameters; } /** * Gets the var map. * * @return the var map */ Map<Integer, Integer> getVarMap() { return Collections.unmodifiableMap(varMap); } }