![JAR search and dependency download from the Maven repository](/logo.png)
com.qitsoft.qimono.internal.modifiers.ConstructorsModifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qimono Show documentation
Show all versions of qimono Show documentation
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