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 Public 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 Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with JBOP. If not, see <http://www.gnu.org/licenses/>. */ package de.tuberlin.uebb.jbop.optimizer.array; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodNode; import de.tuberlin.uebb.jbop.exception.JBOPClassException; import de.tuberlin.uebb.jbop.optimizer.IClassNodeAware; import de.tuberlin.uebb.jbop.optimizer.IInputObjectAware; import de.tuberlin.uebb.jbop.optimizer.IOptimizer; import de.tuberlin.uebb.jbop.optimizer.annotations.ImmutableArray; import de.tuberlin.uebb.jbop.optimizer.utils.NodeHelper; import de.tuberlin.uebb.jbop.optimizer.var.GetFieldChainInliner; /** * Inlines the value of an array (class field) at position i, so that further optimizationsteps * could handle these calls as constant. * * eg: * * <pre> * @ImmtuableArray * private final double[] d = {1.0, 2.0, 3.0}; * ... * double dv = d[2] * </pre> * * becomes * * <pre> * @ImmtuableArray * private final double[] d = {1.0, 2.0, 3.0}; * ... * double dv = 3.0 * </pre> * * In bytecode this means * * <pre> * aload 0 * getfield d * iconst2 [or iload i] * daload * </pre> * * becomes one of * * <pre> * ldc2w x * or * dconstx * </pre> * * depending on the real size of d * * @author Christopher Ewest */ public class FieldArrayValueInliner implements IOptimizer, IInputObjectAware, IClassNodeAware { private final ArrayList<NonNullArrayValue> nonNullArrayValues = new ArrayList<>(); private boolean optimized = false; private Object instance; private ClassNode classNode; @Override public boolean isOptimized() { return optimized; } @Override public InsnList optimize(final InsnList original, final MethodNode method) throws JBOPClassException { optimized = false; final ListIterator<AbstractInsnNode> iterator = original.iterator(); final ArrayHelper arrayHelper = new ArrayHelper(); while (iterator.hasNext()) { final AbstractInsnNode aload = iterator.next(); if (!arrayHelper.isArrayInstruction(classNode, aload, ImmutableArray.class)) { continue; } if (arrayHelper.isIndexEmpty()) { continue; } handleValue(original, aload, arrayHelper, iterator); } return original; } private void moveIterator(final ListIterator<AbstractInsnNode> iterator, final ArrayHelper arrayHelper) { while (iterator.hasNext()) { if (iterator.next() == arrayHelper.getLastNode()) { iterator.previous(); break; } } } private void handleValue(final InsnList newList, final AbstractInsnNode aload, final ArrayHelper arrayHelper, final ListIterator<AbstractInsnNode> iterator) throws JBOPClassException { final Object value = arrayHelper.getValue(instance); final AbstractInsnNode replacementNode; if (value == null) { replacementNode = new InsnNode(Opcodes.ACONST_NULL); } else if ((value instanceof Number)) { replacementNode = NodeHelper.getInsnNodeFor((Number) value); } else if (value instanceof String) { replacementNode = new LdcInsnNode(value); } else { moveIterator(iterator, arrayHelper); final GetFieldChainInliner fieldChainInliner = new GetFieldChainInliner(); fieldChainInliner.setIterator(iterator); fieldChainInliner.setInputObject(value); fieldChainInliner.optimize(newList, null); if (fieldChainInliner.isOptimized()) { // the new valuenode is already put in the list, the chain-nodes are consumed. // but aload, getfield... have to be removed yet. replacementNode = null; } else { // this is not a value we can handle, but its definitly not null. final NonNullArrayValue arrayValue = new NonNullArrayValue(aload, arrayHelper.getFieldNode(), arrayHelper.getIndexes(), arrayHelper.getArrayloads()); nonNullArrayValues.add(arrayValue); return; } } replaceNodes(newList, aload, arrayHelper, replacementNode, iterator); } private void replaceNodes(final InsnList newList, final AbstractInsnNode aload, final ArrayHelper arrayHelper, final AbstractInsnNode replacementNode, final ListIterator<AbstractInsnNode> iterator) { if (replacementNode != null) { newList.insert(arrayHelper.getLastLoad(), replacementNode); } newList.remove(aload); for (final AbstractInsnNode node : arrayHelper.getIndexes()) { newList.remove(node); } for (final AbstractInsnNode node : arrayHelper.getArrayloads()) { newList.remove(node); } if (replacementNode != null) { iterator.next(); } newList.remove(arrayHelper.getFieldNode()); optimized = true; } /** * Gets the {@link NonNullArrayValue}s that were found. * * @return the non null array values */ public List<NonNullArrayValue> getNonNullArrayValues() { return Collections.unmodifiableList(nonNullArrayValues); } @Override public void setInputObject(final Object input) { instance = input; } @Override public void setClassNode(final ClassNode classNode) { this.classNode = classNode; } }