cz.advel.stack.instrument.InstrumentMethod Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stack-alloc Show documentation
Show all versions of stack-alloc Show documentation
Allocate Java objects on method stack instead of program heap.
The newest version!
/*
* 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.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.SimpleVerifier;
/**
*
* @author jezek2
*/
class InstrumentMethod extends MethodNode {
final private static String STACK_NAME = Type.getInternalName(Stack.class);
final private static String STACK_ALLOC_CLASS_DESC = Type.getMethodDescriptor(Type.getType(Object.class), new Type[] { Type.getType(Class.class) } );
final private static String STACK_ALLOC_OBJECT_DESC = Type.getMethodDescriptor(Type.getType(Object.class), new Type[] { Type.getType(Object.class) } );
final private static String STATIC_ALLOC_DESC = Type.getDescriptor(StaticAlloc.class);
// Matthias Mann's Continuations library, see: http://www.matthiasmann.de/content/view/24/26/
final private static String CONTINUATIONS_SUSPEND_EXECUTION_NAME = "de/matthiasmann/continuations/SuspendExecution";
final private Instrumenter instr;
final private InstrumentClass inscls;
final private String className;
final private ClassVisitor cv;
private List frames;
private boolean disableAllocation;
private boolean staticAllocation = false;
boolean emitMethod = true;
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
@SuppressWarnings("unchecked")
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;
// GS: Java 8's compiler introduces LineNumberNode and LabelNode between LdcInsnNode and MethodInsnNode, skip them
if (insnBefore instanceof LineNumberNode) {
insnBefore = insnBefore.getPrevious();
}
if (insnBefore instanceof LabelNode) {
insnBefore = insnBefore.getPrevious();
}
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+")");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy