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

lithium.classloadertest.impl.UnfinalizingClassTransformer Maven / Gradle / Ivy

The newest version!
package lithium.classloadertest.impl;

import lithium.classloadertest.common.TransformingURLClassLoader.ClassTransformer;

import net.sf.cglib.asm.AnnotationVisitor;
import net.sf.cglib.asm.Attribute;
import net.sf.cglib.asm.ClassReader;
import net.sf.cglib.asm.ClassVisitor;
import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.FieldVisitor;
import net.sf.cglib.asm.Label;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;

/**
 * Transforms the bytes of a class to remove all final designation on Classes, Methods and Fields as 
 * well as making private and protected members and the class itself public.  Useful when creating 
 * a fresh classloader as it will make the specified classes more testable with mocking and proxying frameworks.
 * 
 * @author jeff.collins
 */
public class UnfinalizingClassTransformer implements ClassTransformer {

	@Override
	public byte[] transformClass(byte[] inputClass) {
		ClassReader classReader = new ClassReader(inputClass);
		ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
		UnfinalizingClassVisitor ufcv = new UnfinalizingClassVisitor(classWriter);
		classReader.accept(ufcv, 0);
		return classWriter.toByteArray();
	}		
	
	/**
	 *  
	 * @author jeff.collins
	 *
	 */
	private class UnfinalizingClassVisitor implements ClassVisitor {
		private final ClassVisitor cv;
		private String className;
		
		public UnfinalizingClassVisitor(ClassVisitor cv) {
			this.cv = cv;
		}
		
		private int removeFinalAndMakePublic(int access) {
			// change modifier.
			if ((access & Opcodes.ACC_PRIVATE) == Opcodes.ACC_PRIVATE)
				access -= Opcodes.ACC_PRIVATE;
			if ((access & Opcodes.ACC_PROTECTED) == Opcodes.ACC_PROTECTED)
				access -= Opcodes.ACC_PROTECTED;
			access |= Opcodes.ACC_PUBLIC;
			if ((access & Opcodes.ACC_FINAL) != 0) {
				access -= Opcodes.ACC_FINAL;
			}
			return access;
		}
		
		@Override
		public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
			className = name;
			cv.visit(version, removeFinalAndMakePublic(access), name, signature, superName, interfaces);			
		}
		
		@Override
		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
			return cv.visitAnnotation(desc, visible);
		}
		
		@Override
		public void	visitAttribute(Attribute attr) {
			cv.visitAttribute(attr);
		}
		
		@Override
		public void	visitEnd() {	        
			cv.visitEnd();
		}

		@Override
		public FieldVisitor	visitField(int access, String name, String desc, String signature, Object value) {
			return cv.visitField(removeFinalAndMakePublic(access), name, desc, signature, value);
		}
		
		@Override
		public void	visitInnerClass(String name, String outerName, String innerName, int access) {
			cv.visitInnerClass(name, outerName, innerName, removeFinalAndMakePublic(access));
		}
		
		@Override
		public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
			MethodVisitor mv = cv.visitMethod(removeFinalAndMakePublic(access), name, desc, signature, exceptions);
			return new UnprivatizingMethodVisitor(mv, className);
		}
		
		@Override
		public void	visitOuterClass(String owner, String name, String desc) {
			cv.visitOuterClass(owner, name, desc);
		}
		
		@Override
		public void visitSource(String source, String debug) {
			cv.visitSource(source, debug);
		}
	}

	/*
	 * Modifies calls to private methods to use InvokeVirtual instead of InvokeSpecial.  Without this,
	 * private helper methods in a test class will not by overridden by dynamically created proxies, and instead
	 * the private method will be called directly.
	 */
	class UnprivatizingMethodVisitor implements MethodVisitor {
		private final MethodVisitor mv;
		private final String className;
		
		public UnprivatizingMethodVisitor(MethodVisitor mv, String className) {
			this.mv = mv;
			this.className = className;
		}

		@Override
		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
			return mv.visitAnnotation(desc, visible);
		}

		@Override
		public AnnotationVisitor visitAnnotationDefault() {
			return mv.visitAnnotationDefault();
		}

		@Override
		public void visitAttribute(Attribute attr) {
			mv.visitAttribute(attr);
		}

		@Override
		public void visitCode() {
			mv.visitCode();
		}

		@Override
		public void visitEnd() {
			mv.visitEnd();
		}

		@Override
		public void visitFieldInsn(int opcode, String owner, String name, String desc) {
			mv.visitFieldInsn(opcode, owner, name, desc);
		}

		@Override
		public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
			mv.visitFrame(type, nLocal, local, nStack, stack);
		}

		@Override
		public void visitIincInsn(int var, int increment) {
			mv.visitIincInsn(var, increment);
		}

		@Override
		public void visitInsn(int opcode) {
			mv.visitInsn(opcode);
		}

		@Override
		public void visitIntInsn(int opcode, int operand) {
			mv.visitIntInsn(opcode, operand);
		}

		@Override
		public void visitJumpInsn(int opcode, Label label) {
			mv.visitJumpInsn(opcode, label);
		}

		@Override
		public void visitLabel(Label label) {
			mv.visitLabel(label);
		}

		@Override
		public void visitLdcInsn(Object cst) {
			mv.visitLdcInsn(cst);
		}

		@Override
		public void visitLineNumber(int line, Label start) {
			mv.visitLineNumber(line, start);
		}

		@Override
		public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
			mv.visitLocalVariable(name, desc, signature, start, end, index);
		}

		@Override
		public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
			mv.visitLookupSwitchInsn(dflt, keys, labels);
		}

		@Override
		public void visitMaxs(int maxStack, int maxLocals) {
			mv.visitMaxs(maxStack, maxLocals);
		}

		@Override
		public void visitMethodInsn(int opcode, String owner, String name, String desc) {
			if (opcode == Opcodes.INVOKESPECIAL && !name.equals("") && className.equals(owner)) {
				// if the method we're calling is private (because of the invokespecial designation, and
				// is not a constructor, and not from another class, change to calling with InvokeVirtual 
				// which will allow use of the virtual tables, and thus overriding by a proxy subclass...
				opcode = Opcodes.INVOKEVIRTUAL;
			}
			mv.visitMethodInsn(opcode, owner, name, desc);
		}

		@Override
		public void visitMultiANewArrayInsn(String desc, int dims) {
			mv.visitMultiANewArrayInsn(desc, dims);
		}

		@Override
		public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
			return mv.visitParameterAnnotation(parameter, desc, visible);
		}

		@Override
		public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
			mv.visitTableSwitchInsn(min, max, dflt, labels);
		}

		@Override
		public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
			mv.visitTryCatchBlock(start, end, handler, type);
		}

		@Override
		public void visitTypeInsn(int opcode, String type) {
			mv.visitTypeInsn(opcode, type);
		}

		@Override
		public void visitVarInsn(int opcode, int var) {
			mv.visitVarInsn(opcode, var);
		}
		
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy