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

com.qitsoft.qimono.internal.modifiers.ConstructorsModifier Maven / Gradle / Ivy

Go to download

The framework library base on Mockito used to stub methods with private visibility, final methods or static methods. Also this library could be used to assign/retrieve values from private fields and to substitute date and time in tests.

The newest version!
/*
 * Copyright (C) 2011 QitSoft Inc.
 *
 * This file is part of Qimono.
 *
 * Qimono is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package com.qitsoft.qimono.internal.modifiers;

import org.objectweb.asm.MethodAdapter;
import com.qitsoft.qimono.QiMockito;
import org.objectweb.asm.Type;
import com.qitsoft.qimono.internal.ClassModifierUtils;
import com.qitsoft.qimono.internal.ClassModifierAdapter;
import com.qitsoft.qimono.internal.QimonoClassLoader;
import com.qitsoft.qimono.internal.RegistryMethodAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;

import static org.objectweb.asm.Opcodes.*;

/**
 * Modifies constructor invocations.
 * This class is uncompleted and left to be implemented in further versions.
 * 
 * @author Serghei Soloviov 
 */
public class ConstructorsModifier extends ClassModifierAdapter {

    private List methods = new ArrayList();
    private ClassDecl classDecl;
    
    /**
     * Configures the {@link com.qitsoft.qimono.internal.QimonoClassLoader}. It adds
     * itself as a class modifier and adds synthetic modifier.
     * 
     * @param classloader the class loader to configure.
     */
    public static void configureClassloader(QimonoClassLoader classloader) {
        ConstructorsModifier modifier = new ConstructorsModifier();
        classloader.addClassModifier(modifier);
        classloader.addSyntheticClassModifier(new SyntheticConstructorsAdderAdapter(modifier));
    }

    /**
     * {@inheritDoc }
     */
    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        classDecl = new ClassDecl(access, name, signature, superName, interfaces);
        methods.clear();
        super.visit(version, access, name, signature, superName, interfaces);
    }

    /**
     * {@inheritDoc }
     */
    @Override
    public MethodVisitor visitMethod(int access, String name, String decl, String signature, String[] exceptions) {
        
        if ("".equals(name)) {
            methods.add(new MethodDecl(access, name, decl, signature, exceptions, null));
        }
        
//        return new InstantiationsInterceptorAdapter(
//                classDecl,
//                super.visitMethod(access, name, decl, signature, exceptions), 
//                new MethodNode(access, name, decl, signature, exceptions));
        return new MethodAdapter(super.visitMethod(access, name, decl, signature, exceptions)) {

            private final static int STATE_NONE = 0;
            private final static int STATE_NEW = 1;
            private final static int STATE_INSTANTIATE = 2;
            
            private int state = STATE_NONE;
            private String type;
            private RegistryMethodAdapter registryMethodAdapter;

            @Override
            public void visitTypeInsn(int opcode, String type) {
                if (opcode == NEW) {
                    state = STATE_NEW;
                    this.type = type;
                    registryMethodAdapter = new RegistryMethodAdapter();
                } else {
                    super.visitTypeInsn(opcode, type);
                }
            }

            @Override
            public void visitFieldInsn(int i, String string, String string1, String string2) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitFieldInsn(i, string, string1, string2);
                } else {
                    super.visitFieldInsn(i, string, string1, string2);
                }
            }

            @Override
            public void visitFrame(int i, int i1, Object[] os, int i2, Object[] os1) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitFrame(i, i1, os, i2, os1);
                } else {
                    super.visitFrame(i, i1, os, i2, os1);
                }
            }

            @Override
            public void visitIincInsn(int i, int i1) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitIincInsn(i, i1);
                } else {
                    super.visitIincInsn(i, i1);
                }
            }

            @Override
            public void visitInsn(int opcode) {
                if (state == STATE_NEW && opcode == DUP) {
                    state = STATE_INSTANTIATE;
                } else if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitInsn(opcode);
                } else {
                    super.visitInsn(opcode);
                }
            }

            @Override
            public void visitIntInsn(int i, int i1) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitIntInsn(i, i1);
                } else {
                    super.visitIntInsn(i, i1);
                }
            }

            @Override
            public void visitJumpInsn(int i, Label label) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitJumpInsn(i, label);
                } else {
                    super.visitJumpInsn(i, label);
                }
            }

            @Override
            public void visitLabel(Label label) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitLabel(label);
                } else {
                    super.visitLabel(label);
                }
            }

            @Override
            public void visitLdcInsn(Object o) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitLdcInsn(o);
                } else {
                    super.visitLdcInsn(o);
                }
            }

            @Override
            public void visitLineNumber(int i, Label label) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitLineNumber(i, label);
                } else {
                    super.visitLineNumber(i, label);
                }
            }

            @Override
            public void visitLocalVariable(String string, String string1, String string2, Label label, Label label1, int i) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitLocalVariable(string, string1, string2, label, label1, i);
                } else {
                    super.visitLocalVariable(string, string1, string2, label, label1, i);
                }
            }

            @Override
            public void visitLookupSwitchInsn(Label label, int[] ints, Label[] labels) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitLookupSwitchInsn(label, ints, labels);
                } else {
                    super.visitLookupSwitchInsn(label, ints, labels);
                }
            }

            @Override
            public void visitMultiANewArrayInsn(String string, int i) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitMultiANewArrayInsn(string, i);
                } else {
                    super.visitMultiANewArrayInsn(string, i);
                }
            }

            @Override
            public void visitTableSwitchInsn(int i, int i1, Label label, Label[] labels) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitTableSwitchInsn(i, i1, label, labels);
                } else {
                    super.visitTableSwitchInsn(i, i1, label, labels);
                }
            }

            @Override
            public void visitTryCatchBlock(Label label, Label label1, Label label2, String string) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitTryCatchBlock(label, label1, label2, string);
                } else {
                    super.visitTryCatchBlock(label, label1, label2, string);
                }
            }

            @Override
            public void visitVarInsn(int i, int i1) {
                if (state == STATE_INSTANTIATE) {
                    registryMethodAdapter.visitVarInsn(i, i1);
                } else {
                    super.visitVarInsn(i, i1);
                }
            }
            
            

            
            
            @Override
            public void visitMethodInsn(int opcode, String owner, String name, String desc) {
                if ("".equals(name) && owner.equals(type) && state == STATE_INSTANTIATE) {
                    String className = classDecl.name;
                    String classDesc = "L"+className+";";
                    Type classType = Type.getType(classDesc);
                    String syntheticClassName = "qimono/synthetic/"+className;
                    
                    mv.visitLdcInsn(classType);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;");
                    mv.visitTypeInsn(CHECKCAST, Type.getInternalName(QimonoClassLoader.class));
                    mv.visitLdcInsn(classType);
                    mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(QimonoClassLoader.class), "getSyntheticMock", "(Ljava/lang/Class;)Ljava/lang/Object;");
                    mv.visitTypeInsn(CHECKCAST, syntheticClassName);
                    
                    mv.visitMethodInsn(INVOKEVIRTUAL, syntheticClassName, "$construct", desc.replace(")V", ")"+classDesc));
                } else {
                    super.visitMethodInsn(opcode, owner, name, desc);
                }
                
            }
            
        };
    }
    
    /**
     * The Synthetic Class Modifier which created the required infrastructure to be able to 
     * mock constructors.
     */
    private static class SyntheticConstructorsAdderAdapter extends ClassModifierAdapter {

        private ConstructorsModifier modifier;

        public SyntheticConstructorsAdderAdapter(ConstructorsModifier modifier) {
            this.modifier = modifier;
        }

        @Override
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
            super.visit(version, access, name, signature, superName, interfaces);
            
            for(MethodDecl method : modifier.methods) {
                Type[] argTypes = Type.getArgumentTypes(method.desc);
                String newDesc = Type.getMethodDescriptor(Type.getType("L"+modifier.classDecl.name+";"), argTypes);
                
                
                MethodVisitor mv = super.visitMethod(ACC_PUBLIC, "$construct", newDesc, method.signature, method.exceptions);
                
                mv.visitCode();
                mv.visitTypeInsn(NEW, modifier.classDecl.name);
                mv.visitInsn(DUP);
                ClassModifierUtils.createLoadArgs(method.desc, mv, 1);
                mv.visitMethodInsn(INVOKESPECIAL, modifier.classDecl.name, method.name, method.desc);
                mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(QiMockito.class), "spy", "(Ljava/lang/Object;)Ljava/lang/Object;");
                mv.visitTypeInsn(CHECKCAST, modifier.classDecl.name);
                mv.visitInsn(ARETURN);
                mv.visitMaxs(0, 0);
                mv.visitEnd();
            }
        }
        
        

    }

    /**
     * Modifies the class to intercept constructor invocations.
     */
    private static class InstantiationsInterceptorAdapter extends MethodAdapter {

        private MethodNode node;
        private MethodVisitor mv;
        private ClassDecl classDecl;
        
        public InstantiationsInterceptorAdapter(ClassDecl classDecl, MethodVisitor mv, MethodNode node) {
            super(node);
            this.classDecl = classDecl;
            this.mv = mv;
            this.node = node;
        }

        @Override
        public void visitEnd() {
            super.visitEnd();
            
            List> nodeParts = splitInstructionsByInstantiations(node.instructions);
            boolean isInstantiation = false;
            ListIterator> iterator = nodeParts.listIterator();
            while(iterator.hasNext()) {
                List nodes = iterator.next();
                if (isInstantiation) {
                    String desc = ((MethodInsnNode)nodes.get(nodes.size()-1)).desc;
                    
                    List newNodes = new ArrayList();
                    newNodes.add(new LdcInsnNode(Type.getType("L"+classDecl.name+";")));
                    newNodes.add(new TypeInsnNode(CHECKCAST, Type.getInternalName(QimonoClassLoader.class)));
                    newNodes.add(new LdcInsnNode(Type.getType("L"+classDecl.name+";")));
                    newNodes.add(new MethodInsnNode(INVOKEVIRTUAL, Type.getInternalName(QimonoClassLoader.class), "getSyntheticMock", "(Ljava/lang/Class;)Ljava/lang/Object;"));
                    newNodes.add(new TypeInsnNode(CHECKCAST, "qimono/synthetic/"+classDecl.name));
                    int objVar = node.maxLocals;
                    newNodes.add(new IntInsnNode(ASTORE, objVar));
                    newNodes.add(new IntInsnNode(ALOAD, objVar));
                    LabelNode hasntSyntheticLabel = new LabelNode();
                    newNodes.add(new JumpInsnNode(IFNONNULL, hasntSyntheticLabel));
                    for(int i=1;i nodes : nodeParts) {
                for(AbstractInsnNode n : nodes) {
                    node.instructions.add(n);
                }
            }
            node.maxLocals = 0;
            node.maxStack = 0;
            node.accept(mv);
        }

        private List> splitInstructionsByInstantiations(InsnList instructions) {
            List> result = new ArrayList>();
            
            ListIterator iterator = instructions.iterator();
            List nodes = new ArrayList();
            String type = null;
            while(iterator.hasNext()) {
                AbstractInsnNode node = iterator.next();
                if (isInsnNew(node)) {
                    result.add(nodes);
                    nodes = new ArrayList();
                    type = ((TypeInsnNode)node).desc;
                    nodes.add(node);
                } else if (isInstantiate(node, type)) {
                    nodes.add(node);
                    result.add(nodes);
                    type = null;
                    nodes = new ArrayList();
                } else {
                    nodes.add(node);
                }
            }
            result.add(nodes);
            
            return result;
        }

        private boolean isInsnNew(AbstractInsnNode node) {
            return node instanceof TypeInsnNode && ((TypeInsnNode)node).getOpcode() == NEW;
        }

        private boolean isInstantiate(AbstractInsnNode node, String type) {
            if (type == null) {
                return false;
            }
            
            return node instanceof MethodInsnNode 
                    && ((MethodInsnNode)node).owner.equals(type) 
                    &&  ((MethodInsnNode)node).name.equals("");
        }

        
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy