/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2010, Stefan Hepp (stefan@stefant.org). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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.jopdesign.common.tools; import com.jopdesign.common.ClassInfo; import com.jopdesign.common.FieldInfo; import com.jopdesign.common.MemberInfo; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.bcel.Annotation; import com.jopdesign.common.bcel.AnnotationAttribute; import com.jopdesign.common.bcel.AnnotationElement; import com.jopdesign.common.bcel.AnnotationElementValue; import com.jopdesign.common.bcel.CustomAttribute; import com.jopdesign.common.bcel.EnclosingMethod; import com.jopdesign.common.bcel.ParameterAnnotationAttribute; import com.jopdesign.common.graphutils.ClassVisitor; import com.jopdesign.common.graphutils.DescendingClassTraverser; import com.jopdesign.common.graphutils.EmptyClassElementVisitor; import com.jopdesign.common.misc.JavaClassFormatError; import org.apache.bcel.Constants; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.Constant; import org.apache.bcel.classfile.ConstantClass; import org.apache.bcel.classfile.ConstantDouble; import org.apache.bcel.classfile.ConstantFieldref; import org.apache.bcel.classfile.ConstantFloat; import org.apache.bcel.classfile.ConstantInteger; import org.apache.bcel.classfile.ConstantInterfaceMethodref; import org.apache.bcel.classfile.ConstantLong; import org.apache.bcel.classfile.ConstantMethodref; import org.apache.bcel.classfile.ConstantNameAndType; import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.ConstantString; import org.apache.bcel.classfile.ConstantUtf8; import org.apache.bcel.classfile.ConstantValue; import org.apache.bcel.classfile.InnerClass; import org.apache.bcel.classfile.InnerClasses; import org.apache.bcel.classfile.Signature; import org.apache.bcel.classfile.SourceFile; import org.apache.bcel.classfile.StackMap; import org.apache.bcel.classfile.StackMapEntry; import org.apache.bcel.classfile.StackMapType; import org.apache.bcel.classfile.Synthetic; import org.apache.bcel.classfile.Unknown; import org.apache.bcel.generic.CPInstruction; import org.apache.bcel.generic.ClassGen; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.EmptyVisitor; import org.apache.bcel.generic.FieldGen; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.MethodGen; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * This helper class rebuilds the constantpool of a ClassInfo, and should only be used by ClassInfo itself. * * @author Stefan Hepp (stefan@stefant.org) */ public class ConstantPoolRebuilder implements ClassVisitor { public static Constant copyConstant(Map<Integer,Integer> idMap, Constant c) { if (c instanceof ConstantClass) { int index = ((ConstantClass)c).getNameIndex(); return new ConstantClass(idMap.get(index)); } else if (c instanceof ConstantFieldref) { int clsIdx = ((ConstantFieldref)c).getClassIndex(); int nameIdx = ((ConstantFieldref)c).getNameAndTypeIndex(); return new ConstantFieldref(idMap.get(clsIdx), idMap.get(nameIdx)); } else if (c instanceof ConstantMethodref) { int clsIdx = ((ConstantMethodref)c).getClassIndex(); int nameIdx = ((ConstantMethodref)c).getNameAndTypeIndex(); return new ConstantMethodref(idMap.get(clsIdx), idMap.get(nameIdx)); } else if (c instanceof ConstantInterfaceMethodref) { int clsIdx = ((ConstantInterfaceMethodref)c).getClassIndex(); int nameIdx = ((ConstantInterfaceMethodref)c).getNameAndTypeIndex(); return new ConstantInterfaceMethodref(idMap.get(clsIdx), idMap.get(nameIdx)); } else if (c instanceof ConstantString) { int index = ((ConstantString)c).getStringIndex(); return new ConstantString(idMap.get(index)); } else if (c instanceof ConstantInteger) { return new ConstantInteger((ConstantInteger) c); } else if (c instanceof ConstantFloat) { return new ConstantFloat((ConstantFloat) c); } else if (c instanceof ConstantLong) { return new ConstantLong((ConstantLong) c); } else if (c instanceof ConstantDouble) { return new ConstantDouble((ConstantDouble) c); } else if (c instanceof ConstantNameAndType) { int nameIdx = ((ConstantNameAndType)c).getNameIndex(); int sigIdx = ((ConstantNameAndType)c).getSignatureIndex(); return new ConstantNameAndType(idMap.get(nameIdx), idMap.get(sigIdx)); } else if (c instanceof ConstantUtf8) { return new ConstantUtf8((ConstantUtf8) c); } throw new JavaClassFormatError("Unknown constant type "+c); } private class AttributeVisitor extends EmptyClassElementVisitor { private final ConstantPool cp; private AttributeVisitor(ConstantPool cp) { this.cp = cp; } @Override public void visitInnerClasses(ClassInfo classInfo, InnerClasses obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); for (InnerClass ic : obj.getInnerClasses()) { ic.setInnerClassIndex(mapIndex(ic.getInnerClassIndex())); ic.setOuterClassIndex(mapIndex(ic.getOuterClassIndex())); ic.setInnerNameIndex(mapIndex(ic.getInnerNameIndex())); } } @Override public void visitSourceFile(ClassInfo classInfo, SourceFile obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); obj.setSourceFileIndex(mapIndex(obj.getSourceFileIndex())); } @Override public void visitEnclosingMethod(ClassInfo classInfo, EnclosingMethod obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); obj.setClassIndex(mapIndex(obj.getClassIndex())); } @Override public void visitConstantValue(FieldInfo fieldInfo, ConstantValue obj) { throw new AssertionError("Working on FieldGen " +fieldInfo+", ConstantValue should not be present."); } @Override public void visitStackMap(MethodInfo methodInfo, StackMap obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); for (StackMapEntry e : obj.getStackMap()) { e.setConstantPool(cp); for (StackMapType t : e.getTypesOfLocals()) { t.setConstantPool(cp); if (t.getIndex() != -1 && t.getType() == Constants.ITEM_Object) { t.setIndex(mapIndex(t.getIndex())); } } for (StackMapType t : e.getTypesOfStackItems()) { t.setConstantPool(cp); if (t.getIndex() != -1 && t.getType() == Constants.ITEM_Object) { t.setIndex(mapIndex(t.getIndex())); } } } } @Override public void visitSignature(MemberInfo memberInfo, Signature obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); obj.setSignatureIndex(mapIndex(obj.getSignatureIndex())); } @Override public void visitDeprecated(MemberInfo memberInfo, org.apache.bcel.classfile.Deprecated obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); } @Override public void visitSynthetic(MemberInfo memberInfo, Synthetic obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); } @Override public void visitAnnotation(MemberInfo memberInfo, AnnotationAttribute obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); for (Annotation a : obj.getAnnotations()) { visitAnnotation(a); } } @Override public void visitParameterAnnotation(MemberInfo memberInfo, ParameterAnnotationAttribute obj) { obj.setConstantPool(cp); obj.setNameIndex(mapIndex(obj.getNameIndex())); for (int i = 0; i < obj.getNumParameters(); i++) { for (Annotation a : obj.getAnnotations(i)) { visitAnnotation(a); } } } @Override public void visitUnknown(MemberInfo memberInfo, Unknown obj, boolean isCodeAttribute) { throw new JavaClassFormatError("Unsupported attribute in " +memberInfo); } @Override public void visitCustomAttribute(MemberInfo memberInfo, CustomAttribute obj, boolean isCodeAttribute) { throw new JavaClassFormatError("Unsupported attribute in " +memberInfo); } private void visitAnnotation(Annotation a) { a.setConstantPool(cp); a.setTypeIndex((short)mapIndex(a.getTypeIndex())); for (AnnotationElement e : a.getElements()) { e.setConstantPool(cp); e.setNameIndex((short)mapIndex(e.getNameIndex())); AnnotationElementValue v = e.getValue(); v.setConstantPool(cp); if (v.isConstValue()) { v.setConstValueIndex((short)mapIndex(v.getConstValueIndex())); } else { throw new JavaClassFormatError("Unsupported annotation value type: "+v); } } } } private ConstantPoolGen newPool; private final Map<Integer, Integer> idMap; public ConstantPoolRebuilder() { idMap = new HashMap<Integer, Integer>(); } @Override public boolean visitClass(ClassInfo classInfo) { classInfo.rebuildConstantPool(this); return true; } @Override public void finishClass(ClassInfo classInfo) { } public ConstantPoolGen createNewConstantPool(ConstantPoolGen oldPool, Set<Integer> usedIndices) { // We add all used entries to the new pool in the same order as in the old pool // to avoid indices getting larger than before Integer[] ids = new ArrayList<Integer>(usedIndices).toArray(new Integer[usedIndices.size()]); Arrays.sort(ids); // First thing we need is a map, since we need to relink the constantpool entries idMap.clear(); // Note that index 0 is not valid, so skip it List<Constant> constants = new ArrayList<Constant>(usedIndices.size()+1); constants.add(null); int newPos = 1; for (int id : ids) { Constant c = oldPool.getConstant(id); // we cannot use newPool.addConstant here, because this would add all referenced constants too, // and we do not want that.. constants.add(c); idMap.put(id, newPos++); if (c instanceof ConstantLong || c instanceof ConstantDouble) { // reserve an additional slot for those constants.add(null); newPos++; } } // now we create new constants and map the references Constant[] newConstants = new Constant[constants.size()]; for (int i=1; i < constants.size(); i++) { Constant c = constants.get(i); if (c == null) continue; newConstants[i] = copyConstant(idMap, c); } newPool = new ConstantPoolGen(newConstants); return newPool; } public void updateClassGen(ClassInfo classInfo, ClassGen classGen) { classGen.setConstantPool(newPool); // Update name and superclass name index, everything else is stored by value in ClassGen classGen.setClassName(classGen.getClassName()); classGen.setSuperclassName(classGen.getSuperclassName()); updateAttributes(classInfo, classGen.getAttributes()); } public void updateMethodGen(MethodInfo methodInfo, MethodGen methodGen) { methodGen.setConstantPool(newPool); if (methodInfo.hasCode()) { // update all instructions InstructionList il = methodInfo.getCode().getInstructionList(); class InstructionVisitor extends EmptyVisitor { @Override public void visitCPInstruction(CPInstruction obj) { obj.setIndex(mapIndex(obj.getIndex())); } } InstructionVisitor iv = new InstructionVisitor(); for (InstructionHandle ih : il.getInstructionHandles()) { ih.getInstruction().accept(iv); } updateAttributes(methodInfo, methodGen.getCodeAttributes()); } updateAttributes(methodInfo, methodGen.getAttributes()); } public void updateFieldGen(FieldInfo fieldInfo, FieldGen fieldGen) { fieldGen.setConstantPool(newPool); // FieldGen stores everything else by value, not by index updateAttributes(fieldInfo, fieldGen.getAttributes()); } private void updateAttributes(MemberInfo memberInfo, Attribute[] attributes) { AttributeVisitor visitor = new AttributeVisitor(newPool.getConstantPool()); new DescendingClassTraverser(visitor).visitAttributes(memberInfo, attributes); } private int mapIndex(int oldIndex) { if (oldIndex == 0) return 0; // we could also lookup the old constant in the old pool and try add it to the new one // to get the index, if we do not want idMap to be a field //Constant c = oldPool.getConstant(oldIndex); //return newPool.addConstant(c, oldPool); return idMap.get(oldIndex); } }