All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cz.advel.stack.instrument.StackGenerator Maven / Gradle / Ivy

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 java.lang.reflect.Method;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

class StackGenerator implements Opcodes {
	
	/**
	 * A private constructor to inhibit instantiation of this class.
	 */
	private StackGenerator() {
		// do nothing
	}

	private static int getParentDistance(Class concrete, Class parent) {
		int cnt = 0;
		while (concrete != null) {
			if (concrete == parent) {
				break;
			}
			cnt++;
			concrete = concrete.getSuperclass();
		}
		return cnt;
	}
	
	public static Method findGetMethodType(String type) {
		try {
			Class cls = Class.forName(type.replace('/', '.'));
			
			Method bestMethod = null;
			int bestDist = Integer.MAX_VALUE;
			
			for (Method method : cls.getMethods()) {
				if (method.getName().equals("set") && method.getParameterTypes().length == 1) {
					if (method.getParameterTypes()[0].isAssignableFrom(cls)) {
						int dist = getParentDistance(cls, method.getParameterTypes()[0]);
						if (bestMethod == null || dist < bestDist) {
							bestMethod = method;
							bestDist = dist;
						}
					}
				}
			}
			
			if (bestMethod == null) {
				throw new IllegalStateException("can't find set method for "+cls);
			}
			
			return bestMethod;
		}
		catch (ClassNotFoundException e) {
			throw new IllegalStateException(e);
		}
	}

	public static byte[] generateStackClass(Instrumenter instr, String[] types) {
		ClassWriter cw = new ClassWriter(0);
		FieldVisitor fv;
		MethodVisitor mv;

		cw.visit(V1_5, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, instr.getStackInternalName(), null, "java/lang/Object", null);

		cw.visitInnerClass(instr.getStackInternalName()+"$1", null, null, ACC_FINAL + ACC_STATIC);

		// fields:
		if (instr.isSingleThread()) {
			fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "INSTANCE", "L"+instr.getStackInternalName()+";", null, null);
			fv.visitEnd();
		}
		else {
			fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "threadLocal", "Ljava/lang/ThreadLocal;", null, null);
			fv.visitEnd();
		}
		
		for (String type : types) {
			String mangled = Instrumenter.mangleInternalName(type);
			
			fv = cw.visitField(ACC_PRIVATE, "list$"+mangled, "Ljava/util/ArrayList;", null, null);
			fv.visitEnd();
			
			fv = cw.visitField(ACC_PRIVATE, "stack$"+mangled, "[I", null, null);
			fv.visitEnd();
			
			fv = cw.visitField(ACC_PRIVATE, "count$"+mangled, "I", null, null);
			fv.visitEnd();
			
			fv = cw.visitField(ACC_PRIVATE, "pos$"+mangled, "I", null, null);
			fv.visitEnd();
		}
		
		// constructor:
		{
			mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
			mv.visitCode();
			mv.visitVarInsn(ALOAD, 0);
			mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V");
			
			for (String type : types) {
				String mangled = Instrumenter.mangleInternalName(type);
				
				mv.visitVarInsn(ALOAD, 0);
				mv.visitTypeInsn(NEW, "java/util/ArrayList");
				mv.visitInsn(DUP);
				mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "", "()V");
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "list$"+mangled, "Ljava/util/ArrayList;");

				mv.visitVarInsn(ALOAD, 0);
				mv.visitIntInsn(BIPUSH, 16);
				mv.visitIntInsn(NEWARRAY, T_INT);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");

				mv.visitVarInsn(ALOAD, 0);
				mv.visitInsn(ICONST_0);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "count$"+mangled, "I");

				mv.visitVarInsn(ALOAD, 0);
				mv.visitInsn(ICONST_0);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "pos$"+mangled, "I");
			}
			
			mv.visitInsn(RETURN);
			mv.visitMaxs(3, 1);
			mv.visitEnd();
		}
		
		// static stack get method:
		if (!instr.isSingleThread()) {
			mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "get", "()L"+instr.getStackInternalName()+";", null, null);
			mv.visitCode();
			mv.visitFieldInsn(GETSTATIC, instr.getStackInternalName(), "threadLocal", "Ljava/lang/ThreadLocal;");
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ThreadLocal", "get", "()Ljava/lang/Object;");
			mv.visitTypeInsn(CHECKCAST, instr.getStackInternalName());
			mv.visitInsn(ARETURN);
			mv.visitMaxs(1, 0);
			mv.visitEnd();
		}
		
		// per-type methods:
		for (String type : types) {
			String mangled = Instrumenter.mangleInternalName(type);
			
			{
				mv = cw.visitMethod(ACC_PUBLIC, "push$"+mangled, "()V", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "count$"+mangled, "I");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");
				mv.visitInsn(ARRAYLENGTH);
				Label l0 = new Label();
				mv.visitJumpInsn(IF_ICMPNE, l0);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitMethodInsn(INVOKESPECIAL, instr.getStackInternalName(), "resize$"+mangled, "()V");
				mv.visitLabel(l0);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitInsn(DUP);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "count$"+mangled, "I");
				mv.visitInsn(DUP_X1);
				mv.visitInsn(ICONST_1);
				mv.visitInsn(IADD);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "count$"+mangled, "I");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "pos$"+mangled, "I");
				mv.visitInsn(IASTORE);
				mv.visitInsn(RETURN);
				mv.visitMaxs(5, 1);
				mv.visitEnd();
			}
			{
				mv = cw.visitMethod(ACC_PRIVATE, "resize$"+mangled, "()V", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");
				mv.visitInsn(ARRAYLENGTH);
				mv.visitInsn(ICONST_1);
				mv.visitInsn(ISHL);
				mv.visitIntInsn(NEWARRAY, T_INT);
				mv.visitVarInsn(ASTORE, 1);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");
				mv.visitInsn(ICONST_0);
				mv.visitVarInsn(ALOAD, 1);
				mv.visitInsn(ICONST_0);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");
				mv.visitInsn(ARRAYLENGTH);
				mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitVarInsn(ALOAD, 1);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");
				mv.visitInsn(RETURN);
				mv.visitMaxs(5, 2);
				mv.visitEnd();
			}
			{
				mv = cw.visitMethod(ACC_PUBLIC, "pop$"+mangled, "()V", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "stack$"+mangled, "[I");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitInsn(DUP);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "count$"+mangled, "I");
				mv.visitInsn(ICONST_1);
				mv.visitInsn(ISUB);
				mv.visitInsn(DUP_X1);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "count$"+mangled, "I");
				mv.visitInsn(IALOAD);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "pos$"+mangled, "I");
				mv.visitInsn(RETURN);
				mv.visitMaxs(5, 1);
				mv.visitEnd();
			}
			{
				mv = cw.visitMethod(ACC_PUBLIC, "get$"+mangled, "()L"+type+";", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "pos$"+mangled, "I");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "list$"+mangled, "Ljava/util/ArrayList;");
				mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "size", "()I");
				Label l0 = new Label();
				mv.visitJumpInsn(IF_ICMPNE, l0);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "list$"+mangled, "Ljava/util/ArrayList;");
				mv.visitTypeInsn(NEW, type);
				mv.visitInsn(DUP);
				mv.visitMethodInsn(INVOKESPECIAL, type, "", "()V");
				mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "add", "(Ljava/lang/Object;)Z");
				mv.visitInsn(POP);
				mv.visitLabel(l0);
				mv.visitVarInsn(ALOAD, 0);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "list$"+mangled, "Ljava/util/ArrayList;");
				mv.visitVarInsn(ALOAD, 0);
				mv.visitInsn(DUP);
				mv.visitFieldInsn(GETFIELD, instr.getStackInternalName(), "pos$"+mangled, "I");
				mv.visitInsn(DUP_X1);
				mv.visitInsn(ICONST_1);
				mv.visitInsn(IADD);
				mv.visitFieldInsn(PUTFIELD, instr.getStackInternalName(), "pos$"+mangled, "I");
				mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "get", "(I)Ljava/lang/Object;");
				mv.visitTypeInsn(CHECKCAST, type);
				mv.visitInsn(ARETURN);
				mv.visitMaxs(5, 1);
				mv.visitEnd();
			}
			{
				mv = cw.visitMethod(ACC_PUBLIC, "get$"+mangled, "(L"+type+";)L"+type+";", null, null);
				mv.visitCode();
				mv.visitVarInsn(ALOAD, 0);
				mv.visitMethodInsn(INVOKEVIRTUAL, instr.getStackInternalName(), "get$"+mangled, "()L"+type+";");
				mv.visitVarInsn(ASTORE, 2);
				mv.visitVarInsn(ALOAD, 2);
				mv.visitVarInsn(ALOAD, 1);
				
				Method m = findGetMethodType(type);
				mv.visitMethodInsn(INVOKEVIRTUAL, type, "set", "(L"+Type.getInternalName(m.getParameterTypes()[0])+";)V");
				
				mv.visitVarInsn(ALOAD, 2);
				mv.visitInsn(ARETURN);
				mv.visitMaxs(2, 3);
				mv.visitEnd();
			}
		}
		
		// class init:
		{
			mv = cw.visitMethod(ACC_STATIC, "", "()V", null, null);
			mv.visitCode();
			if (instr.isSingleThread()) {
				mv.visitTypeInsn(NEW, instr.getStackInternalName());
				mv.visitInsn(DUP);
				mv.visitMethodInsn(INVOKESPECIAL, instr.getStackInternalName(), "", "()V");
				mv.visitFieldInsn(PUTSTATIC, instr.getStackInternalName(), "INSTANCE", "L"+instr.getStackInternalName()+";");
				mv.visitInsn(RETURN);
				mv.visitMaxs(2, 0);
			}
			else {
				mv.visitTypeInsn(NEW, instr.getStackInternalName()+"$1");
				mv.visitInsn(DUP);
				mv.visitMethodInsn(INVOKESPECIAL, instr.getStackInternalName()+"$1", "", "()V");
				mv.visitFieldInsn(PUTSTATIC, instr.getStackInternalName(), "threadLocal", "Ljava/lang/ThreadLocal;");
				if (!instr.isIsolated()) {
					mv.visitFieldInsn(GETSTATIC, instr.getStackInternalName(), "threadLocal", "Ljava/lang/ThreadLocal;");
					mv.visitMethodInsn(INVOKESTATIC, Instrumenter.STACK_NAME, "internalRegisterThreadLocal", "(Ljava/lang/ThreadLocal;)V");
				}
				mv.visitInsn(RETURN);
				mv.visitMaxs(2, 0);
			}
			mv.visitEnd();
		}
		cw.visitEnd();

		return cw.toByteArray();
	}
	
	public static byte[] generateStackClass1(Instrumenter instr) {
		ClassWriter cw = new ClassWriter(0);
		MethodVisitor mv;

		cw.visit(V1_5, ACC_FINAL + ACC_SUPER, instr.getStackInternalName()+"$1", null, "java/lang/ThreadLocal", null);

		cw.visitOuterClass(instr.getStackInternalName(), null, null);

		cw.visitInnerClass(instr.getStackInternalName()+"$1", null, null, ACC_FINAL + ACC_STATIC);

		{
			mv = cw.visitMethod(0, "", "()V", null, null);
			mv.visitCode();
			mv.visitVarInsn(ALOAD, 0);
			mv.visitMethodInsn(INVOKESPECIAL, "java/lang/ThreadLocal", "", "()V");
			mv.visitInsn(RETURN);
			mv.visitMaxs(1, 1);
			mv.visitEnd();
		}
		{
			mv = cw.visitMethod(ACC_PROTECTED, "initialValue", "()Ljava/lang/Object;", null, null);
			mv.visitCode();
			mv.visitTypeInsn(NEW, instr.getStackInternalName());
			mv.visitInsn(DUP);
			mv.visitMethodInsn(INVOKESPECIAL, instr.getStackInternalName(), "", "()V");
			mv.visitInsn(ARETURN);
			mv.visitMaxs(2, 1);
			mv.visitEnd();
		}
		cw.visitEnd();

		return cw.toByteArray();
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy