org.evosuite.instrumentation.StringTransformation.java Source code

Java tutorial

Introduction

Here is the source code for org.evosuite.instrumentation.StringTransformation.java

Source

/**
 * Copyright (C) 2011,2012 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 Public License as published by the Free Software Foundation,
 * either version 3 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 Public License for more details.
 * 
 * You should have received a copy of the GNU Public License along with
 * EvoSuite. If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * 
 */
package org.evosuite.instrumentation;

import java.util.List;
import java.util.ListIterator;
import java.util.regex.Matcher;

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.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * StringTransformation class.
 * </p>
 * 
 * @author fraser
 */
public class StringTransformation {

    private static Logger logger = LoggerFactory.getLogger(StringTransformation.class);

    ClassNode cn;

    /**
     * <p>
     * Constructor for StringTransformation.
     * </p>
     * 
     * @param cn
     *            a {@link org.objectweb.asm.tree.ClassNode} object.
     */
    public StringTransformation(ClassNode cn) {
        this.cn = cn;
    }

    /**
     * <p>
     * transform
     * </p>
     * 
     * @return a {@link org.objectweb.asm.tree.ClassNode} object.
     */
    @SuppressWarnings("unchecked")
    public ClassNode transform() {
        List<MethodNode> methodNodes = cn.methods;
        for (MethodNode mn : methodNodes) {
            if (transformMethod(mn)) {
                mn.maxStack++;
            }
        }

        return cn;
    }

    /**
     * Replace boolean-returning method calls on String classes
     * 
     * @param mn
     */
    @SuppressWarnings("unchecked")
    private boolean transformStrings(MethodNode mn) {
        logger.info("Current method: {}", mn.name);
        boolean changed = false;
        ListIterator<AbstractInsnNode> iterator = mn.instructions.iterator();
        while (iterator.hasNext()) {
            AbstractInsnNode node = iterator.next();
            if (node instanceof MethodInsnNode) {
                MethodInsnNode min = (MethodInsnNode) node;
                if (min.owner.equals("java/lang/String")) {
                    if (min.name.equals("equals")) {
                        changed = true;

                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringEquals",
                                Type.getMethodDescriptor(Type.INT_TYPE,
                                        new Type[] { Type.getType(String.class), Type.getType(Object.class) }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                        /*
                                MethodInsnNode equalCheck = new MethodInsnNode(
                                        Opcodes.INVOKESTATIC,
                                        Type.getInternalName(BooleanHelper.class),
                                        "StringEqualsCharacterDistance",
                                        Type.getMethodDescriptor(Type.DOUBLE_TYPE,
                                                                 new Type[] {
                                                                         Type.getType(String.class),
                                                                         Type.getType(Object.class) }));
                                mn.instructions.insertBefore(node, equalCheck);
                                mn.instructions.insertBefore(node, new LdcInsnNode(0.0));
                                mn.instructions.insertBefore(node, new InsnNode(Opcodes.DCMPG));
                                mn.instructions.remove(node);
                                */
                        TransformationStatistics.transformedStringComparison();

                    } else if (min.name.equals("equalsIgnoreCase")) {
                        changed = true;
                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringEqualsIgnoreCase",
                                Type.getMethodDescriptor(Type.INT_TYPE,
                                        new Type[] { Type.getType(String.class), Type.getType(String.class) }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                        TransformationStatistics.transformedStringComparison();

                    } else if (min.name.equals("startsWith")) {
                        changed = true;
                        if (min.desc.equals("(Ljava/lang/String;)Z")) {
                            mn.instructions.insertBefore(node, new InsnNode(Opcodes.ICONST_0));
                        }
                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringStartsWith",
                                Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { Type.getType(String.class),
                                        Type.getType(String.class), Type.INT_TYPE }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                        TransformationStatistics.transformedStringComparison();

                    } else if (min.name.equals("endsWith")) {
                        changed = true;
                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringEndsWith",
                                Type.getMethodDescriptor(Type.INT_TYPE,
                                        new Type[] { Type.getType(String.class), Type.getType(String.class) }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                        TransformationStatistics.transformedStringComparison();

                    } else if (min.name.equals("isEmpty")) {
                        changed = true;
                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringIsEmpty",
                                Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { Type.getType(String.class) }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                        TransformationStatistics.transformedStringComparison();
                    } else if (min.name.equals("matches")) {
                        changed = true;
                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringMatches",
                                Type.getMethodDescriptor(Type.INT_TYPE,
                                        new Type[] { Type.getType(String.class), Type.getType(String.class) }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                        TransformationStatistics.transformedStringComparison();
                    } else if (min.name.equals("regionMatches")) {
                        Type[] argumentTypes = Type.getArgumentTypes(min.desc);
                        if (argumentTypes.length == 4) {
                            changed = true;
                            MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                    Type.getInternalName(BooleanHelper.class), "StringRegionMatches",
                                    Type.getMethodDescriptor(Type.INT_TYPE,
                                            new Type[] { Type.getType(String.class), Type.INT_TYPE,
                                                    Type.getType(String.class), Type.INT_TYPE, Type.INT_TYPE }));
                            mn.instructions.insertBefore(node, equalCheck);
                            mn.instructions.remove(node);
                            TransformationStatistics.transformedStringComparison();

                        } else if (argumentTypes.length == 5) {
                            changed = true;
                            MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                    Type.getInternalName(BooleanHelper.class), "StringRegionMatches",
                                    Type.getMethodDescriptor(Type.INT_TYPE,
                                            new Type[] { Type.getType(String.class), Type.BOOLEAN_TYPE,
                                                    Type.INT_TYPE, Type.getType(String.class), Type.INT_TYPE,
                                                    Type.INT_TYPE }));
                            mn.instructions.insertBefore(node, equalCheck);
                            mn.instructions.remove(node);
                            TransformationStatistics.transformedStringComparison();
                        }
                    }

                } else if (min.owner.equals("java/util/regex/Pattern")) {
                    if (min.name.equals("matches")) {
                        changed = true;
                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringMatchRegex",
                                Type.getMethodDescriptor(Type.INT_TYPE, new Type[] { Type.getType(String.class),
                                        Type.getType(CharSequence.class) }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                    }
                } else if (min.owner.equals("java/util/regex/Matcher")) {
                    if (min.name.equals("matches")) {
                        changed = true;
                        MethodInsnNode equalCheck = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                Type.getInternalName(BooleanHelper.class), "StringMatchRegex",
                                Type.getMethodDescriptor(Type.INT_TYPE,
                                        new Type[] { Type.getType(Matcher.class) }));
                        mn.instructions.insertBefore(node, equalCheck);
                        mn.instructions.remove(node);
                    }
                }
            }
        }
        return changed;
    }

    private static boolean isStringMethod(AbstractInsnNode node) {
        if (node.getOpcode() == Opcodes.INVOKESTATIC) {
            MethodInsnNode methodInsnNode = (MethodInsnNode) node;
            return methodInsnNode.owner.equals(Type.getInternalName(BooleanHelper.class))
                    && methodInsnNode.name.startsWith("String");
        }
        return false;
    }

    /**
     * <p>
     * transformMethod
     * </p>
     * 
     * @param mn
     *            a {@link org.objectweb.asm.tree.MethodNode} object.
     * @return a boolean.
     */
    public boolean transformMethod(MethodNode mn) {
        boolean changed = transformStrings(mn);
        if (changed) {
            try {
                mn.maxStack++;
                Analyzer a = new Analyzer(new StringBooleanInterpreter());
                a.analyze(cn.name, mn);
                Frame[] frames = a.getFrames();
                AbstractInsnNode node = mn.instructions.getFirst();
                boolean done = false;
                while (!done) {
                    if (node == mn.instructions.getLast())
                        done = true;
                    AbstractInsnNode next = node.getNext();
                    int index = mn.instructions.indexOf(node);
                    if (index >= frames.length)
                        break;
                    Frame current = frames[index];
                    if (current == null)
                        break;
                    int size = current.getStackSize();
                    if (node.getOpcode() == Opcodes.IFNE) {
                        JumpInsnNode branch = (JumpInsnNode) node;
                        if (current.getStack(size - 1) == StringBooleanInterpreter.STRING_BOOLEAN
                                || isStringMethod(node.getPrevious())) {
                            logger.info("IFNE -> IFGT");
                            branch.setOpcode(Opcodes.IFGT);
                            // branch.setOpcode(Opcodes.IFGE);
                        }
                    } else if (node.getOpcode() == Opcodes.IFEQ) {
                        JumpInsnNode branch = (JumpInsnNode) node;
                        if (current.getStack(size - 1) == StringBooleanInterpreter.STRING_BOOLEAN
                                || isStringMethod(node.getPrevious())) {
                            logger.info("IFEQ -> IFLE");
                            branch.setOpcode(Opcodes.IFLE);
                            // branch.setOpcode(Opcodes.IFNE);
                        }
                    } else if (node.getOpcode() == Opcodes.IF_ICMPEQ) {
                        JumpInsnNode branch = (JumpInsnNode) node;
                        if (current.getStack(size - 2) == StringBooleanInterpreter.STRING_BOOLEAN
                                || isStringMethod(node.getPrevious().getPrevious())) {
                            if (node.getPrevious().getOpcode() == Opcodes.ICONST_0) {
                                branch.setOpcode(Opcodes.IFLE);
                                mn.instructions.remove(node.getPrevious());
                            } else if (node.getPrevious().getOpcode() == Opcodes.ICONST_1) {
                                branch.setOpcode(Opcodes.IFGT);
                                mn.instructions.remove(node.getPrevious());
                            }
                        }
                    } else if (node.getOpcode() == Opcodes.IF_ICMPNE) {
                        JumpInsnNode branch = (JumpInsnNode) node;
                        if (current.getStack(size - 2) == StringBooleanInterpreter.STRING_BOOLEAN
                                || isStringMethod(node.getPrevious().getPrevious())) {
                            if (node.getPrevious().getOpcode() == Opcodes.ICONST_0) {
                                branch.setOpcode(Opcodes.IFGT);
                                mn.instructions.remove(node.getPrevious());
                            } else if (node.getPrevious().getOpcode() == Opcodes.ICONST_1) {
                                branch.setOpcode(Opcodes.IFLE);
                                mn.instructions.remove(node.getPrevious());
                            }
                        }
                    } else if (node.getOpcode() == Opcodes.IRETURN) {
                        if (current.getStack(size - 1) == StringBooleanInterpreter.STRING_BOOLEAN
                                || isStringMethod(node.getPrevious())) {
                            logger.info("IFEQ -> IFLE");
                            MethodInsnNode n = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                    Type.getInternalName(BooleanHelper.class), "intToBoolean",
                                    Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[] { Type.INT_TYPE }));

                            mn.instructions.insertBefore(node, n);
                        }
                    }
                    node = next;
                }
            } catch (Exception e) {
                logger.warn("EXCEPTION DURING STRING TRANSFORMATION: {}", e);
                e.printStackTrace();
                return changed;
            }
        }
        return changed;
    }
}