/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) 2004,2005, Flavius Gruian Copyright (C) 2005-2008, Martin Schoeberl (martin@jopdesign.com) 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.build; import com.jopdesign.tools.JopInstr; import org.apache.bcel.classfile.Constant; import org.apache.bcel.classfile.ConstantClass; import org.apache.bcel.classfile.ConstantFieldref; import org.apache.bcel.classfile.ConstantNameAndType; import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.Field; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.ACONST_NULL; import org.apache.bcel.generic.BasicType; import org.apache.bcel.generic.CPInstruction; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.FieldInstruction; import org.apache.bcel.generic.GETFIELD; import org.apache.bcel.generic.GETSTATIC; import org.apache.bcel.generic.INVOKESPECIAL; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.InvokeInstruction; import org.apache.bcel.generic.MONITORENTER; import org.apache.bcel.generic.MONITOREXIT; import org.apache.bcel.generic.MethodGen; import org.apache.bcel.generic.NOP; import org.apache.bcel.generic.PUTFIELD; import org.apache.bcel.generic.PUTSTATIC; import org.apache.bcel.generic.ReferenceType; import org.apache.bcel.generic.Type; import org.apache.bcel.util.InstructionFinder; import java.util.Iterator; /** * @author Flavius, Martin * */ public class ReplaceNativeAndCPIdx extends JOPizerVisitor { // Why do we use a ConstantPoolGen and a ConstantPool? private ConstantPoolGen cpoolgen; private ConstantPool cp; public ReplaceNativeAndCPIdx(OldAppInfo jz) { super(jz); } public void visitJavaClass(JavaClass clazz) { super.visitJavaClass(clazz); Method[] methods = clazz.getMethods(); cp = clazz.getConstantPool(); cpoolgen = new ConstantPoolGen(cp); for (int i = 0; i < methods.length; i++) { if (!(methods[i].isAbstract() || methods[i].isNative())) { Method m = replace(methods[i]); OldMethodInfo mi = getCli().getMethodInfo(m.getName()+m.getSignature()); // set new method also in MethodInfo mi.setMethod(m); if (m != null) { methods[i] = m; } } } } private Method replace(Method method) { MethodGen mg = new MethodGen(method, clazz.getClassName(), cpoolgen); InstructionList il = mg.getInstructionList(); InstructionFinder f = new InstructionFinder(il); String methodId = method.getName() + method.getSignature(); OldMethodInfo mi = getCli().getMethodInfo(methodId); // find invokes first and replace call to Native by // JOP native instructions. String invokeStr = "InvokeInstruction"; for (Iterator i = f.search(invokeStr); i.hasNext();) { InstructionHandle[] match = (InstructionHandle[]) i.next(); InstructionHandle first = match[0]; InvokeInstruction ii = (InvokeInstruction) first.getInstruction(); if (ii.getClassName(cpoolgen).equals(JOPizer.nativeClass)) { short opid = (short) JopInstr.getNative(ii .getMethodName(cpoolgen)); if (opid == -1) { System.err.println(method.getName() + ": cannot locate " + ii.getMethodName(cpoolgen) + ". Replacing with NOP."); first.setInstruction(new NOP()); } else { first.setInstruction(new NativeInstruction(opid, (short) 1)); ((JOPizer) ai).outTxt.println("\t"+first.getPosition()); // since the new instruction is of length 1 and // the replaced invokespecial was of length 3 // then we remove pc+2 and pc+1 from the MGCI info if (JOPizer.dumpMgci) { il.setPositions(); int pc = first.getPosition(); // important: take the high one first GCRTMethodInfo.removePC(pc + 2, mi); GCRTMethodInfo.removePC(pc + 1, mi); } } } if (ii instanceof INVOKESPECIAL) { // not an initializer if (!ii.getMethodName(cpoolgen).equals("<init>")) { // check if this is a super invoke // TODO this is just a hack, use InvokeSite.isInvokeSuper() when this is ported to the new framework! boolean isSuper = false; String declaredType = ii.getClassName(cpoolgen); JopClassInfo cls = getCli(); OldClassInfo superClass = cls.superClass; while (superClass != null) { if (superClass.clazz.getClassName().equals(declaredType)) { isSuper = true; break; } if ("java.lang.Object".equals(superClass.clazz.getClassName())) { break; } superClass = superClass.superClass; } if (isSuper) { Integer idx = ii.getIndex(); int new_index = getCli().cpoolUsed.indexOf(idx) + 1; first.setInstruction(new JOPSYS_INVOKESUPER((short)new_index)); // System.err.println("invokesuper "+ii.getClassName(cpoolgen)+"."+ii.getMethodName(cpoolgen)); } } } } if (JOPizer.CACHE_INVAL) { f = new InstructionFinder(il); // find volatile reads and insert cache invalidation bytecode String fieldInstr = "GETFIELD|GETSTATIC|PUTFIELD|PUTSTATIC"; for(Iterator i = f.search(fieldInstr); i.hasNext(); ) { InstructionHandle[] match = (InstructionHandle[])i.next(); InstructionHandle ih = match[0]; FieldInstruction fi = (FieldInstruction) ih.getInstruction(); JavaClass jc = JOPizer.jz.cliMap.get(fi.getClassName(cpoolgen)).clazz; Field field = null; while (field == null) { Field [] fields = jc.getFields(); for (int k = 0; k < fields.length; k++) { if (fields[k].getName().equals(fi.getFieldName(cpoolgen))) { field = fields[k]; break; } } if (field == null) { try { jc = jc.getSuperClass(); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new Error(); } } } if (field.isVolatile()) { if (field.getType().getSize() < 2) { if (fi instanceof GETFIELD || fi instanceof GETSTATIC) { ih.setInstruction(new InvalidateInstruction()); ih = il.append(ih, fi); } } else { // this only works because we do not throw a // NullPointerException for monitorenter/-exit! ih.setInstruction(new ACONST_NULL()); ih = il.append(ih, new MONITORENTER()); ih = il.append(ih, fi); ih = il.append(ih, new ACONST_NULL()); ih = il.append(ih, new MONITOREXIT()); } } } } f = new InstructionFinder(il); // find instructions that access the constant pool // and replace the index by the new value from ClassInfo String cpInstr = "CPInstruction"; for (Iterator it = f.search(cpInstr); it.hasNext();) { InstructionHandle[] match = (InstructionHandle[]) it.next(); InstructionHandle ih = match[0]; CPInstruction cpii = (CPInstruction) ih.getInstruction(); int index = cpii.getIndex(); // we have to grab the information before we change // the CP index. FieldInstruction fi = null; Type ft = null; if (cpii instanceof FieldInstruction) { fi = (FieldInstruction) ih.getInstruction(); ft = fi.getFieldType(cpoolgen); } Integer idx = new Integer(index); // pos is the new position in the reduced constant pool // idx is the position in the 'original' unresolved cpool int pos = getCli().cpoolUsed.indexOf(idx); int new_index = pos + 1; // replace index by the offset for getfield // and putfield and by address for getstatic and putstatic if (cpii instanceof GETFIELD || cpii instanceof PUTFIELD || cpii instanceof GETSTATIC || cpii instanceof PUTSTATIC) { // we use the offset instead of the CP index new_index = getFieldOffset(cp, index); } else { if (pos == -1) { System.out.println("Error: constant " + index + " " + cpoolgen.getConstant(index) + " not found"); System.out.println("new cpool: " + getCli().cpoolUsed); System.out.println("original cpool: " + cpoolgen); System.exit(-1); } } // set new index, position starts at // 1 as cp points to the length of the pool cpii.setIndex(new_index); // Added field instruction replacement // by Rasmus and extended by Martin // Replace reference and long/double field bytecodes // with 'special' bytecodes. if (cpii instanceof FieldInstruction) { boolean isRef = ft instanceof ReferenceType; boolean isLong = ft == BasicType.LONG || ft == BasicType.DOUBLE; if (fi instanceof GETSTATIC) { if (isRef) { ih.setInstruction(new GETSTATIC_REF((short) new_index)); } else if (isLong) { ih.setInstruction(new GETSTATIC_LONG((short) new_index)); } } else if (fi instanceof PUTSTATIC) { if (isRef) { if (!com.jopdesign.build.JOPizer.USE_RTTM) { ih.setInstruction(new PUTSTATIC_REF((short) new_index)); } } else if (isLong) { ih.setInstruction(new PUTSTATIC_LONG((short) new_index)); } } else if (fi instanceof GETFIELD) { if (isRef) { ih.setInstruction(new GETFIELD_REF((short) new_index)); } else if (isLong) { ih.setInstruction(new GETFIELD_LONG((short) new_index)); } } else if (fi instanceof PUTFIELD) { if (isRef) { if (!com.jopdesign.build.JOPizer.USE_RTTM) { ih.setInstruction(new PUTFIELD_REF((short) new_index)); } } else if (isLong) { ih.setInstruction(new PUTFIELD_LONG((short) new_index)); } } } } Method m = mg.getMethod(); il.dispose(); return m; } /** * Get field offset: relative offset for object fields, absolute * addresses for static fields. * * TODO: remove the dead code in constant pool replacement * TODO: remove not used constants * @param cp * @param index * @return */ private int getFieldOffset(ConstantPool cp, int index) { // from ClassInfo.resolveCPool Constant co = cp.getConstant(index); int fidx = ((ConstantFieldref) co).getClassIndex(); ConstantClass fcl = (ConstantClass) cp.getConstant(fidx); String fclname = fcl.getBytes(cp).replace('/', '.'); // got the class name int sigidx = ((ConstantFieldref) co).getNameAndTypeIndex(); ConstantNameAndType signt = (ConstantNameAndType) cp .getConstant(sigidx); String sigstr = signt.getName(cp) + signt.getSignature(cp); JopClassInfo clinf = (JopClassInfo) ai.cliMap.get(fclname); int j; boolean found = false; while (!found) { for (j = 0; j < clinf.clft.len; ++j) { if (clinf.clft.key[j].equals(sigstr)) { found = true; return clinf.clft.idx[j]; } } if (!found) { clinf = (JopClassInfo) clinf.superClass; if (clinf == null) { System.out.println("Error: field " + fclname + "." + sigstr + " not found!"); break; } } } System.out.println("Error in getFieldOffset()"); System.exit(-1); return 0; } class GETSTATIC_REF extends FieldInstruction { public GETSTATIC_REF(short index) { super((short) JopInstr.get("getstatic_ref"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class PUTSTATIC_REF extends FieldInstruction { public PUTSTATIC_REF(short index) { super((short) JopInstr.get("putstatic_ref"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class GETFIELD_REF extends FieldInstruction { public GETFIELD_REF(short index) { super((short) JopInstr.get("getfield_ref"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class PUTFIELD_REF extends FieldInstruction { public PUTFIELD_REF(short index) { super((short) JopInstr.get("putfield_ref"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class GETSTATIC_LONG extends FieldInstruction { public GETSTATIC_LONG(short index) { super((short) JopInstr.get("getstatic_long"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class PUTSTATIC_LONG extends FieldInstruction { public PUTSTATIC_LONG(short index) { super((short) JopInstr.get("putstatic_long"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class GETFIELD_LONG extends FieldInstruction { public GETFIELD_LONG(short index) { super((short) JopInstr.get("getfield_long"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class PUTFIELD_LONG extends FieldInstruction { public PUTFIELD_LONG(short index) { super((short) JopInstr.get("putfield_long"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } } class NativeInstruction extends Instruction { public NativeInstruction(short arg0, short arg1) { super(arg0, arg1); } public void accept(org.apache.bcel.generic.Visitor v) { } } class JOPSYS_INVOKESUPER extends InvokeInstruction { public JOPSYS_INVOKESUPER(short index) { super((short) JopInstr.get("invokesuper"), index); } public void accept(org.apache.bcel.generic.Visitor v) { } // could be copied from INVOKESPECIAL public Class[] getExceptions() { return null; } } class InvalidateInstruction extends Instruction { public InvalidateInstruction() { super((short)JopInstr.get("jopsys_inval"), (short)1); } public void accept(org.apache.bcel.generic.Visitor v) { } } }