/* * JStackAlloc (c) 2008 Martin Dvorak * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ package cz.advel.stack.instrument; import cz.advel.stack.Stack; import cz.advel.stack.StaticAlloc; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.objectweb.asm.*; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.*; /** * * @author jezek2 */ class InstrumentMethod extends MethodNode { private static String STACK_NAME = Type.getInternalName(Stack.class); private static String STACK_ALLOC_CLASS_DESC = Type.getMethodDescriptor(Type.getType(Object.class), new Type[] { Type.getType(Class.class) } ); private static String STACK_ALLOC_OBJECT_DESC = Type.getMethodDescriptor(Type.getType(Object.class), new Type[] { Type.getType(Object.class) } ); private static String STATIC_ALLOC_DESC = Type.getDescriptor(StaticAlloc.class); // Matthias Mann's Continuations library, see: http://www.matthiasmann.de/content/view/24/26/ private static String CONTINUATIONS_SUSPEND_EXECUTION_NAME = "de/matthiasmann/continuations/SuspendExecution"; private Instrumenter instr; private InstrumentClass inscls; private String className; private ClassVisitor cv; private List frames; private boolean disableAllocation; private boolean staticAllocation = false; boolean emitMethod = true; public InstrumentMethod(int access, String name, String desc, String signature, String[] exceptions, Instrumenter instr, InstrumentClass inscls, String className, ClassVisitor cv) { super(access, name, desc, signature, exceptions); this.instr = instr; this.inscls = inscls; this.className = className; this.cv = cv; disableAllocation = instr.isDisabled(); } @Override public void visitEnd() { try { // always disable stack allocation in suspendable methods: for (int i=0; i(Arrays.asList(analyzer.getFrames())); Set usedTypes = new HashSet(); boolean createStack = false; AbstractInsnNode insn = instructions.getFirst(); while (insn != null) { if (insn instanceof MethodInsnNode) { MethodInsnNode min = (MethodInsnNode)insn; // check for Stack.alloc(Class): if (min.owner.equals(STACK_NAME) && min.name.equals("alloc") && min.desc.equals(STACK_ALLOC_CLASS_DESC)) { AbstractInsnNode insnBefore = min.getPrevious(); Type type = null; if (insnBefore instanceof LdcInsnNode && ((LdcInsnNode)insnBefore).cst instanceof Type) { type = (Type)((LdcInsnNode)insnBefore).cst; removeInsn(insnBefore); } else { logError("first parameter of Stack.alloc(Class) must be constant"); } if (!staticAllocation) { usedTypes.add(type.getInternalName()); instr.addStackType(type.getInternalName()); } insn = replaceAllocClass(insn, type, stackVar).getNext(); removeInsn(min); // remove redudant checkcast: if (insn instanceof TypeInsnNode) { TypeInsnNode tin = (TypeInsnNode)insn; if (tin.getOpcode() == Opcodes.CHECKCAST && tin.desc.equals(type.getInternalName())) { insn = insn.getNext(); removeInsn(tin); } } continue; } // check for Stack.alloc(Object): if (min.owner.equals(STACK_NAME) && min.name.equals("alloc") && min.desc.equals(STACK_ALLOC_OBJECT_DESC)) { Frame frame = frames.get(instructions.indexOf(insn)); BasicValue value = (BasicValue)frame.getStack(frame.getStackSize() - 1); Type type = value.getType(); if (!staticAllocation) { usedTypes.add(type.getInternalName()); instr.addStackType(type.getInternalName()); } insn = replaceAllocObject(insn, type, stackVar).getNext(); removeInsn(min); // remove redudant checkcast: if (insn instanceof TypeInsnNode) { TypeInsnNode tin = (TypeInsnNode)insn; if (tin.getOpcode() == Opcodes.CHECKCAST && tin.desc.equals(type.getInternalName())) { insn = insn.getNext(); removeInsn(tin); } } continue; } // check for Stack.libraryCleanCurrentThread(): if (min.owner.equals(STACK_NAME) && min.name.equals("libraryCleanCurrentThread") && min.desc.equals("()V")) { if (instr.isDisabled() || instr.isSingleThread()) { insn = insn.getNext(); removeInsn(min); continue; } else { insn = insertInsn(insn, new FieldInsnNode(Opcodes.GETSTATIC, instr.getStackInternalName(), "threadLocal", "Ljava/lang/ThreadLocal;")); insn = insertInsn(insn, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/ThreadLocal", "remove", "()V")); removeInsn(min); continue; } } } insn = insn.getNext(); } if (!disableAllocation && (createStack || !usedTypes.isEmpty())) { // create code for getting stack object: insn = instructions.getFirst(); insertStackVarBefore(insn, stackVar); LabelNode startLabel = new LabelNode(); insertInsnBefore(insn, startLabel); // push/pop used types if any: if (usedTypes.size() > 0) { String[] typesArray = usedTypes.toArray(new String[usedTypes.size()]); insertPush(insn, typesArray, stackVar); while (insn != null) { if (insn instanceof InsnNode && isReturnOpcode(insn.getOpcode())) { insertPop(insn, true, typesArray, stackVar); } insn = insn.getNext(); } // create finally block: LabelNode endLabel = new LabelNode(); insn = instructions.getLast(); insn = insertInsn(insn, endLabel); insn = insertPop(insn, false, typesArray, stackVar); insn = insertInsn(insn, new InsnNode(Opcodes.ATHROW)); tryCatchBlocks.add(new TryCatchBlockNode(startLabel, endLabel, endLabel, null)); } } if (emitMethod) { accept(cv); } } catch (AnalyzerException e) { throw new IllegalStateException(e); } } //////////////////////////////////////////////////////////////////////////// private void removeInsn(AbstractInsnNode insn) { int idx = instructions.indexOf(insn); instructions.remove(insn); frames.remove(idx); } private AbstractInsnNode insertInsn(AbstractInsnNode insn, InsnList list) { if (list.size() == 0) { return insn; } int idx = instructions.indexOf(insn); for (int i=0; i", "()V")); } else if (staticAllocation) { int num = inscls.registerStaticAlloc(type.getInternalName()); list.add(new FieldInsnNode(Opcodes.GETSTATIC, className, "$stackTemp"+num, type.getDescriptor())); } else { list.add(new VarInsnNode(Opcodes.ALOAD, stackVar)); list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, instr.getStackInternalName(), "get$"+Instrumenter.mangleInternalName(type.getInternalName()), "()"+type.getDescriptor())); } return insertInsn(pos, list); } private AbstractInsnNode replaceAllocObject(AbstractInsnNode pos, Type type, int stackVar) { InsnList list = new InsnList(); if (disableAllocation) { list.add(new TypeInsnNode(Opcodes.NEW, type.getInternalName())); list.add(new InsnNode(Opcodes.DUP)); list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, type.getInternalName(), "", "()V")); list.add(new InsnNode(Opcodes.DUP_X1)); list.add(new InsnNode(Opcodes.SWAP)); Method m = StackGenerator.findGetMethodType(type.getInternalName()); list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, type.getInternalName(), "set", "(L"+Type.getInternalName(m.getParameterTypes()[0])+";)V")); } else if (staticAllocation) { int num = inscls.registerStaticAlloc(type.getInternalName()); list.add(new FieldInsnNode(Opcodes.GETSTATIC, className, "$stackTemp"+num, type.getDescriptor())); list.add(new InsnNode(Opcodes.DUP_X1)); list.add(new InsnNode(Opcodes.SWAP)); Method m = StackGenerator.findGetMethodType(type.getInternalName()); list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, type.getInternalName(), "set", "(L"+Type.getInternalName(m.getParameterTypes()[0])+";)V")); } else { list.add(new VarInsnNode(Opcodes.ALOAD, stackVar)); list.add(new InsnNode(Opcodes.SWAP)); list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, instr.getStackInternalName(), "get$"+Instrumenter.mangleInternalName(type.getInternalName()), "("+type.getDescriptor()+")"+type.getDescriptor())); } return insertInsn(pos, list); } private void logError(String msg) { throw new IllegalStateException(msg+" (in class "+className.replace('/', '.')+", method "+name+")"); } }