![JAR search and dependency download from the Maven repository](/logo.png)
org.evosuite.instrumentation.testability.BooleanTestabilityTransformation Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite 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.0 of the License, or
* (at your option) any later version.
*
* EvoSuite 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 Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
/**
*
*/
package org.evosuite.instrumentation.testability;
import java.util.Arrays;
import java.util.List;
import org.evosuite.Properties;
import org.evosuite.coverage.branch.Branch;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.graphs.GraphPool;
import org.evosuite.graphs.cfg.BytecodeAnalyzer;
import org.evosuite.graphs.cfg.BytecodeInstruction;
import org.evosuite.graphs.cfg.BytecodeInstructionPool;
import org.evosuite.instrumentation.BooleanArrayInterpreter;
import org.evosuite.instrumentation.BooleanValueInterpreter;
import org.evosuite.instrumentation.TransformationStatistics;
import org.evosuite.instrumentation.testability.transformer.BitwiseOperatorTransformer;
import org.evosuite.instrumentation.testability.transformer.BooleanArrayIndexTransformer;
import org.evosuite.instrumentation.testability.transformer.BooleanArrayTransformer;
import org.evosuite.instrumentation.testability.transformer.BooleanCallsTransformer;
import org.evosuite.instrumentation.testability.transformer.BooleanDefinitionTransformer;
import org.evosuite.instrumentation.testability.transformer.BooleanDistanceTransformer;
import org.evosuite.instrumentation.testability.transformer.BooleanIfTransformer;
import org.evosuite.instrumentation.testability.transformer.BooleanReturnTransformer;
import org.evosuite.instrumentation.testability.transformer.ImplicitElseTransformer;
import org.evosuite.instrumentation.testability.transformer.InstanceOfTransformer;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
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.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Transform everything Boolean to ints.
*
* This transformation replaces: - TRUE/FALSE with +K/-K - IFEQ/IFNE with
* IFLE/IFGT - Signatures in fields and calls - Inserts calls to remember
* distance of last boolean calculation - Inserts calls to recall distance of
* last boolean calculation when Boolean is used
*
* @author Gordon Fraser
*/
public class BooleanTestabilityTransformation {
public static final Logger logger = LoggerFactory.getLogger(BooleanTestabilityTransformation.class);
private final ClassNode cn;
public final String className;
public Frame[] currentFrames = null;
private MethodNode currentMethodNode = null;
public ClassLoader classLoader;
/**
*
* Constructor for BooleanTestabilityTransformation.
*
*
* @param cn
* a {@link org.objectweb.asm.tree.ClassNode} object.
*/
public BooleanTestabilityTransformation(ClassNode cn, ClassLoader classLoader) {
this.cn = cn;
this.className = cn.name.replace('/', '.');
this.classLoader = classLoader;
}
/**
* Transform all methods and fields
*
* @return a {@link org.objectweb.asm.tree.ClassNode} object.
*/
public ClassNode transform() {
processFields();
processMethods();
clearIntermediateResults();
if (className.equals(Properties.TARGET_CLASS)
|| className.startsWith(Properties.TARGET_CLASS + "$"))
TransformationStatistics.writeStatistics(className);
return cn;
}
@SuppressWarnings("unchecked")
private void clearIntermediateResults() {
List methodNodes = cn.methods;
for (MethodNode mn : methodNodes) {
if ((mn.access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE)
continue;
GraphPool.clearAll(className, mn.name + mn.desc);
BytecodeInstructionPool.clearAll(className, mn.name + mn.desc);
BranchPool.getInstance(classLoader).clear(className, mn.name + mn.desc);
}
}
/**
* Handle transformation of fields defined in this class
*/
@SuppressWarnings("unchecked")
private void processFields() {
List fields = cn.fields;
for (FieldNode field : fields) {
if (DescriptorMapping.getInstance().isTransformedField(className, field.name,
field.desc)) {
String newDesc = transformFieldDescriptor(className, field.name,
field.desc);
logger.info("Transforming field " + field.name + " from " + field.desc
+ " to " + newDesc);
if (!newDesc.equals(field.desc))
TransformationStatistics.transformBooleanField();
field.desc = newDesc;
}
}
}
/**
* Handle transformation of methods defined in this class
*/
@SuppressWarnings("unchecked")
private void processMethods() {
List methodNodes = cn.methods;
for (MethodNode mn : methodNodes) {
if ((mn.access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE)
continue;
if (DescriptorMapping.getInstance().isTransformedMethod(className, mn.name,
mn.desc)) {
logger.info("Transforming signature of method " + mn.name + mn.desc);
transformMethodSignature(mn);
logger.info("Transformed signature to " + mn.name + mn.desc);
}
transformMethod(mn);
}
}
/**
*
* getOriginalNameDesc
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param desc
* a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
public static String getOriginalNameDesc(String className, String methodName,
String desc) {
String key = className.replace('.', '/') + "/" + methodName + desc;
if (DescriptorMapping.getInstance().originalDesc.containsKey(key)) {
logger.debug("Descriptor mapping contains original for " + key);
return DescriptorMapping.getInstance().getOriginalName(className, methodName,
desc)
+ DescriptorMapping.getInstance().originalDesc.get(key);
} else {
logger.debug("Descriptor mapping does not contain original for " + key);
return methodName + desc;
}
}
/**
*
* getOriginalDesc
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param desc
* a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
public static String getOriginalDesc(String className, String methodName, String desc) {
String key = className.replace('.', '/') + "/" + methodName + desc;
if (DescriptorMapping.getInstance().originalDesc.containsKey(key)) {
logger.debug("Descriptor mapping contains original for " + key);
return DescriptorMapping.getInstance().originalDesc.get(key);
} else {
logger.debug("Descriptor mapping does not contain original for " + key);
return desc;
}
}
/**
*
* hasTransformedParameters
*
*
* @param className
* a {@link java.lang.String} object.
* @param methodName
* a {@link java.lang.String} object.
* @param desc
* a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean hasTransformedParameters(String className, String methodName,
String desc) {
String key = className.replace('.', '/') + "/" + methodName + desc;
if (DescriptorMapping.getInstance().originalDesc.containsKey(key)) {
for (Type type : Type.getArgumentTypes(DescriptorMapping.getInstance().originalDesc.get(key))) {
if (type.equals(Type.BOOLEAN_TYPE))
return true;
}
}
return false;
}
/**
*
* isTransformedField
*
*
* @param className
* a {@link java.lang.String} object.
* @param fieldName
* a {@link java.lang.String} object.
* @param desc
* a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean isTransformedField(String className, String fieldName,
String desc) {
return DescriptorMapping.getInstance().isTransformedField(className, fieldName,
desc);
}
/**
* Insert a call to the isNull helper function
*
* @param opcode
* @param position
* @param list
*/
public void insertPushNull(int opcode, JumpInsnNode position, InsnList list) {
int branchId = getBranchID(currentMethodNode, position);
logger.info("Inserting instrumentation for NULL check at branch " + branchId
+ " in method " + currentMethodNode.name);
MethodInsnNode nullCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "isNull",
Type.getMethodDescriptor(Type.INT_TYPE,
new Type[] { Type.getType(Object.class),
Type.INT_TYPE }), false);
list.insertBefore(position, new InsnNode(Opcodes.DUP));
list.insertBefore(position, new LdcInsnNode(opcode));
list.insertBefore(position, nullCheck);
//list.insertBefore(position,
// new LdcInsnNode(getBranchID(currentMethodNode, position)));
insertBranchIdPlaceholder(currentMethodNode, position, branchId);
MethodInsnNode push = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "pushPredicate",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type.INT_TYPE,
Type.INT_TYPE }), false);
list.insertBefore(position, push);
}
/**
* Insert a call to the reference equality check helper function
*
* @param opcode
* @param position
* @param list
*/
public void insertPushEquals(int opcode, JumpInsnNode position, InsnList list) {
MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "isEqual",
Type.getMethodDescriptor(Type.INT_TYPE,
new Type[] { Type.getType(Object.class),
Type.getType(Object.class),
Type.INT_TYPE }), false);
list.insertBefore(position, new InsnNode(Opcodes.DUP2));
list.insertBefore(position, new LdcInsnNode(opcode));
list.insertBefore(position, equalCheck);
//list.insertBefore(position,
// new LdcInsnNode(getBranchID(currentMethodNode, position)));
insertBranchIdPlaceholder(currentMethodNode, position);
MethodInsnNode push = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "pushPredicate",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type.INT_TYPE,
Type.INT_TYPE }), false);
list.insertBefore(position, push);
}
private BytecodeInstruction getBytecodeInstruction(MethodNode mn,
AbstractInsnNode node) {
return BytecodeInstructionPool.getInstance(classLoader).getInstruction(className,
mn.name
+ mn.desc,
node); // TODO: Adapt for classloaders
}
private int getBranchID(MethodNode mn, JumpInsnNode jumpNode) {
assert (mn.instructions.contains(jumpNode));
BytecodeInstruction insn = getBytecodeInstruction(mn, jumpNode);
logger.info("Found instruction: " + insn);
Branch branch = BranchPool.getInstance(classLoader).getBranchForInstruction(insn);
return branch.getActualBranchId();
}
private int getControlDependentBranchID(MethodNode mn, AbstractInsnNode insnNode) {
BytecodeInstruction insn = getBytecodeInstruction(mn, insnNode);
// FIXXME: Handle multiple control dependencies
return insn.getControlDependentBranchId();
}
private int getApproximationLevel(MethodNode mn, AbstractInsnNode insnNode) {
BytecodeInstruction insn = getBytecodeInstruction(mn, insnNode);
// FIXXME: Handle multiple control dependencies
return insn.getCDGDepth();
}
private void insertBranchIdPlaceholder(MethodNode mn, JumpInsnNode jumpNode) {
Label label = new Label();
LabelNode labelNode = new LabelNode(label);
//BooleanTestabilityPlaceholderTransformer.addBranchPlaceholder(label, jumpNode);
mn.instructions.insertBefore(jumpNode, labelNode);
//mn.instructions.insertBefore(jumpNode, new LdcInsnNode(0));
mn.instructions.insertBefore(jumpNode, new LdcInsnNode(getBranchID(mn, jumpNode)));
}
private void insertBranchIdPlaceholder(MethodNode mn, JumpInsnNode jumpNode,
int branchId) {
Label label = new Label();
LabelNode labelNode = new LabelNode(label);
//BooleanTestabilityPlaceholderTransformer.addBranchPlaceholder(label, jumpNode);
mn.instructions.insertBefore(jumpNode, labelNode);
//mn.instructions.insertBefore(jumpNode, new LdcInsnNode(0));
mn.instructions.insertBefore(jumpNode, new LdcInsnNode(branchId));
}
private void insertControlDependencyPlaceholder(MethodNode mn,
AbstractInsnNode insnNode) {
Label label = new Label();
LabelNode labelNode = new LabelNode(label);
//BooleanTestabilityPlaceholderTransformer.addControlDependencyPlaceholder(label,
// insnNode);
mn.instructions.insertBefore(insnNode, labelNode);
//instructions.insertBefore(insnNode, new LdcInsnNode(0));
//mn.instructions.insertBefore(insnNode, new LdcInsnNode(0));
mn.instructions.insertBefore(insnNode, new LdcInsnNode(
getControlDependentBranchID(mn, insnNode)));
mn.instructions.insertBefore(insnNode,
new LdcInsnNode(getApproximationLevel(mn, insnNode)));
logger.info("Control dependent branch id: "
+ getControlDependentBranchID(mn, insnNode));
logger.info("Approximation level: " + getApproximationLevel(mn, insnNode));
}
/**
* Insert a call to the distance function for unary comparison
*
* @param opcode
* @param position
* @param list
*/
public void insertPush(int opcode, JumpInsnNode position, InsnList list) {
list.insertBefore(position, new InsnNode(Opcodes.DUP));
// TODO: We have to put a placeholder here instead of the actual branch ID
// TODO: And then later add another transformation where we replace this with
// actual branch IDs
//list.insertBefore(position,
// new LdcInsnNode(getBranchID(currentMethodNode, position)));
insertBranchIdPlaceholder(currentMethodNode, position);
MethodInsnNode push = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "pushPredicate",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type.INT_TYPE,
Type.INT_TYPE }), false);
list.insertBefore(position, push);
}
/**
* Insert a call to the distance function for binary comparison
*
* @param opcode
* @param position
* @param list
*/
public void insertPush2(int opcode, JumpInsnNode position, InsnList list) {
list.insertBefore(position, new InsnNode(Opcodes.DUP2));
//list.insertBefore(position, new InsnNode(Opcodes.ISUB));
MethodInsnNode sub = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "intSub",
Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { Type.INT_TYPE,
Type.INT_TYPE }), false);
list.insertBefore(position, sub);
insertBranchIdPlaceholder(currentMethodNode, position);
// list.insertBefore(position,
// new LdcInsnNode(getBranchID(currentMethodNode, position)));
MethodInsnNode push = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "pushPredicate",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type.INT_TYPE,
Type.INT_TYPE }), false);
list.insertBefore(position, push);
}
/**
* Insert a call that takes a boolean from the stack, and returns the
* appropriate distance
*
* @param position
* @param list
*/
public void insertGet(AbstractInsnNode position, InsnList list) {
logger.info("Inserting get call");
// Here, branchId is the first control dependency
//list.insertBefore(position,
// new LdcInsnNode(getControlDependentBranchID(currentMethodNode,
// position)));
insertControlDependencyPlaceholder(currentMethodNode, position);
MethodInsnNode get = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "getDistance",
Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { Type.INT_TYPE,
Type.INT_TYPE, Type.INT_TYPE }), false);
list.insert(position, get);
}
/**
* Insert a call that takes a boolean from the stack, and returns the
* appropriate distance
*
* @param position
* @param list
*/
public void insertGetBefore(AbstractInsnNode position, InsnList list) {
logger.info("Inserting get call before");
// Here, branchId is the first control dependency
//list.insertBefore(position,
// new LdcInsnNode(getControlDependentBranchID(currentMethodNode,
// position)));
// insertControlDependencyPlaceholder(currentMethodNode, position);
// branch
// approx
// value
Label label = new Label();
LabelNode labelNode = new LabelNode(label);
//BooleanTestabilityPlaceholderTransformer.addControlDependencyPlaceholder(label,
// insnNode);
currentMethodNode.instructions.insertBefore(position, labelNode);
//instructions.insertBefore(insnNode, new LdcInsnNode(0));
//mn.instructions.insertBefore(insnNode, new LdcInsnNode(0));
currentMethodNode.instructions.insertBefore(position, new LdcInsnNode(
getControlDependentBranchID(currentMethodNode, position)));
currentMethodNode.instructions.insertBefore(position, new InsnNode(Opcodes.SWAP));
currentMethodNode.instructions.insertBefore(position, new LdcInsnNode(
getApproximationLevel(currentMethodNode, position)));
currentMethodNode.instructions.insertBefore(position, new InsnNode(Opcodes.SWAP));
MethodInsnNode get = new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(BooleanHelper.class), "getDistance",
Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { Type.INT_TYPE,
Type.INT_TYPE, Type.INT_TYPE }), false);
list.insertBefore(position, get);
}
public boolean isBooleanOnStack(MethodNode mn, AbstractInsnNode node, int position) {
int insnPosition = mn.instructions.indexOf(node);
if (insnPosition >= currentFrames.length) {
logger.info("Trying to access frame out of scope: " + insnPosition + "/"
+ currentFrames.length);
return false;
}
Frame frame = currentFrames[insnPosition];
return frame.getStack(frame.getStackSize() - 1 - position) == BooleanValueInterpreter.BOOLEAN_VALUE;
}
public boolean isBooleanVariable(int var, MethodNode mn) {
for (Object o : mn.localVariables) {
LocalVariableNode vn = (LocalVariableNode) o;
if (vn.index == var)
return Type.getType(vn.desc).equals(Type.BOOLEAN_TYPE);
}
return false;
}
/**
* This helper function determines whether the boolean on the stack at the
* current position will be stored in a Boolean variable
*
* @param position
* @param mn
* @return
*/
public boolean isBooleanAssignment(AbstractInsnNode position, MethodNode mn) {
AbstractInsnNode node = position.getNext();
logger.info("Checking for ISTORE after boolean");
boolean done = false;
while (!done) {
if (node.getOpcode() == Opcodes.PUTFIELD
|| node.getOpcode() == Opcodes.PUTSTATIC) {
// TODO: Check whether field is static
logger.info("Checking field assignment");
FieldInsnNode fn = (FieldInsnNode) node;
if (Type.getType(DescriptorMapping.getInstance().getFieldDesc(fn.owner,
fn.name,
fn.desc)) == Type.BOOLEAN_TYPE) {
return true;
} else {
return false;
}
} else if (node.getOpcode() == Opcodes.ISTORE) {
logger.info("Found ISTORE after boolean");
VarInsnNode vn = (VarInsnNode) node;
// TODO: Check whether variable at this position is a boolean
if (isBooleanVariable(vn.var, mn)) {
logger.info("Assigning boolean to variable ");
return true;
} else {
logger.info("Variable is not a bool");
return false;
}
} else if (node.getOpcode() == Opcodes.IRETURN) {
logger.info("Checking return value of method " + cn.name + "." + mn.name);
if (DescriptorMapping.getInstance().isTransformedOrBooleanMethod(cn.name,
mn.name,
mn.desc)) {
logger.info("Method returns a bool");
return true;
} else {
logger.info("Method does not return a bool");
return false;
}
} else if (node.getOpcode() == Opcodes.BASTORE) {
// We remove all bytes, so BASTORE is only used for booleans
AbstractInsnNode start = position.getNext();
boolean reassignment = false;
while (start != node) {
if (node instanceof InsnNode) {
reassignment = true;
}
start = start.getNext();
}
logger.info("Possible assignment to array?");
if (reassignment)
return false;
else
return true;
} else if (node instanceof MethodInsnNode) {
// if it is a boolean parameter of a converted method, then it needs to be converted
// Problem: How do we know which parameter it represents?
MethodInsnNode methodNode = (MethodInsnNode) node;
String desc = DescriptorMapping.getInstance().getMethodDesc(methodNode.owner,
methodNode.name,
methodNode.desc);
Type[] types = Type.getArgumentTypes(desc);
if (types.length > 0 && types[types.length - 1] == Type.BOOLEAN_TYPE) {
return true;
} else {
return false;
}
} else if (node.getOpcode() == Opcodes.GOTO
|| node.getOpcode() == Opcodes.ICONST_0
|| node.getOpcode() == Opcodes.ICONST_1 || node.getOpcode() == -1) {
logger.info("Continuing search");
// continue search
} else if (!(node instanceof LineNumberNode || node instanceof FrameNode)) {
logger.info("Search ended with opcode " + node.getOpcode());
return false;
}
if (node != mn.instructions.getLast())
node = node.getNext();
else
done = true;
}
return false;
}
private void generateCDG(MethodNode mn) {
if(BytecodeInstructionPool.getInstance(classLoader).hasMethod(className, mn.name + mn.desc))
return;
BytecodeInstructionPool.getInstance(classLoader).registerMethodNode(mn,
className,
mn.name
+ mn.desc); // TODO: Adapt for multiple classLoaders
BytecodeAnalyzer bytecodeAnalyzer = new BytecodeAnalyzer();
logger.info("Generating initial CFG for method " + mn.name);
try {
bytecodeAnalyzer.analyze(classLoader, className,
mn.name + mn.desc, mn); // TODO
} catch (AnalyzerException e) {
logger.error("Analyzer exception while analyzing " + className + "."
+ mn.name + ": " + e);
e.printStackTrace();
}
// compute Raw and ActualCFG and put both into GraphPool
bytecodeAnalyzer.retrieveCFGGenerator().registerCFGs();
}
/**
* Determine if the signature of the given method needs to be transformed,
* and transform if necessary
*
* @param owner
* @param name
* @param desc
* @return
*/
public String transformMethodDescriptor(String owner, String name, String desc) {
return DescriptorMapping.getInstance().getMethodDesc(owner, name, desc);
}
/**
* Determine if the signature of the given field needs to be transformed,
* and transform if necessary
*
* @param owner
* @param name
* @param desc
* @return
*/
public String transformFieldDescriptor(String owner, String name, String desc) {
return DescriptorMapping.getInstance().getFieldDesc(owner, name, desc);
}
private void transformMethodSignature(MethodNode mn) {
// If the method was declared in java.* then don't instrument
// Otherwise change signature
String newDesc = DescriptorMapping.getInstance().getMethodDesc(className,
mn.name, mn.desc);
if (Type.getReturnType(mn.desc) == Type.BOOLEAN_TYPE
&& Type.getReturnType(newDesc) == Type.INT_TYPE)
TransformationStatistics.transformBooleanReturnValue();
if (Arrays.asList(Type.getArgumentTypes(mn.desc)).contains(Type.BOOLEAN_TYPE)
&& !Arrays.asList(Type.getArgumentTypes(newDesc)).contains(Type.BOOLEAN_TYPE))
TransformationStatistics.transformBooleanParameter();
String newName = DescriptorMapping.getInstance().getMethodName(className,
mn.name, mn.desc);
logger.info("Changing method descriptor from "
+ mn.name
+ mn.desc
+ " to "
+ DescriptorMapping.getInstance().getMethodName(className, mn.name,
mn.desc) + newDesc);
mn.desc = DescriptorMapping.getInstance().getMethodDesc(className, mn.name,
mn.desc);
mn.name = newName;
}
private Frame[] getArrayFrames(MethodNode mn) {
try {
Analyzer a = new Analyzer(new BooleanArrayInterpreter());
a.analyze(cn.name, mn);
return a.getFrames();
} catch (Exception e) {
logger.info("[Array] Error during analysis: " + e);
return null;
}
}
/**
* Apply testability transformation to an individual method
*
* @param mn
*/
private void transformMethod(MethodNode mn) {
logger.info("Transforming method " + mn.name + mn.desc);
//currentCFG = GraphPool.getActualCFG(className, mn.name + mn.desc);
// TODO: Skipping interfaces for now, but will need to handle Booleans in interfaces!
if ((mn.access & Opcodes.ACC_ABSTRACT) == Opcodes.ACC_ABSTRACT)
return;
String origDesc = getOriginalDesc(className, mn.name, mn.desc);
logger.info("Analyzing " + mn.name + " for TT, signature " + origDesc + "/"
+ mn.desc);
try {
Analyzer a = new Analyzer(new BooleanValueInterpreter(origDesc,
(mn.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC));
a.analyze(className, mn);
currentFrames = a.getFrames();
} catch (Exception e) {
logger.info("1. Error during analysis: " + e);
//e.printStackTrace();
// TODO: Handle error
}
generateCDG(mn);
currentMethodNode = mn;
// First expand ifs without else/*
new ImplicitElseTransformer(this).transform(mn);
try {
Analyzer a = new Analyzer(new BooleanValueInterpreter(origDesc,
(mn.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC));
a.analyze(className, mn);
currentFrames = a.getFrames();
} catch (Exception e) {
logger.info("2. Error during analysis: " + e);
//e.printStackTrace();
// TODO: Handle error
}
// BytecodeInstructionPool.reRegisterMethodNode(mn, className, mn.name + mn.desc);
// Replace all bitwise operators
logger.info("Transforming Boolean bitwise operators");
new BitwiseOperatorTransformer(this).transform(mn);
// Transform IFEQ/IFNE to IFLE/IFGT
logger.info("Transforming Boolean IFs");
new BooleanIfTransformer(this).transform(mn);
// Insert call to BooleanHelper.get after ICONST_0/1 or Boolean fields
logger.info("Transforming Boolean definitions");
new BooleanDefinitionTransformer(this).transform(mn);
// Replace all instanceof comparisons
logger.info("Transforming instanceof");
new InstanceOfTransformer().transform(mn);
// Replace all calls to methods/fields returning booleans
new BooleanCallsTransformer(this).transform(mn);
// Transform all flag based comparisons
logger.info("Transforming Boolean distances");
new BooleanDistanceTransformer(this).transform(mn);
mn.maxStack += 3;
// Replace all boolean arrays
new BooleanArrayTransformer().transform(mn);
new BooleanArrayIndexTransformer(getArrayFrames(mn)).transform(mn);
// Replace all boolean return values
logger.info("Transforming Boolean return values");
new BooleanReturnTransformer(this).transform(mn);
// GraphPool.clear(className, mn.name + mn.desc);
// BytecodeInstructionPool.clear(className, mn.name + mn.desc);
// BranchPool.clear(className, mn.name + mn.desc);
// Actually this should be done automatically by the ClassWriter...
// +2 because we might do a DUP2
mn.maxStack += 1;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy