org.mutabilitydetector.checkers.settermethod.AssignmentGuardFinder.java Source code

Java tutorial

Introduction

Here is the source code for org.mutabilitydetector.checkers.settermethod.AssignmentGuardFinder.java

Source

package org.mutabilitydetector.checkers.settermethod;

/*
 * #%L
 * MutabilityDetector
 * %%
 * Copyright (C) 2008 - 2014 Graham Allan
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.objectweb.asm.Opcodes.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.concurrent.Immutable;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.VarInsnNode;

/**
 * 
 * 
 * @author Juergen Fickel
 * @version 02.03.2013
 */
@Immutable
final class AssignmentGuardFinder implements Finder<JumpInsn> {

    private final String candidateName;
    private final ControlFlowBlock controlFlowBlock;

    private AssignmentGuardFinder(final String theCandidateName, final ControlFlowBlock theControlFlowBlock) {
        candidateName = theCandidateName;
        controlFlowBlock = theControlFlowBlock;
    }

    /**
     * Creates a new instance of this class. None of the arguments must be
     * {@code null}.
     * 
     * @param candidateName
     *            name of the lazy variable. Must not be empty!
     * @param controlFlowBlock
     *            the control flow block which is supposed to contain an
     *            {@link AssignmentGuard}.
     * @return a new instance of this class.
     */
    public static AssignmentGuardFinder newInstance(final String candidateName,
            final ControlFlowBlock controlFlowBlock) {
        checkArgument(!candidateName.isEmpty());
        return new AssignmentGuardFinder(candidateName, checkNotNull(controlFlowBlock));
    }

    @Override
    public JumpInsn find() {
        final Collection<JumpInsn> supposedAssignmentGuards = collectSupposedAssignmentGuards();
        if (1 < supposedAssignmentGuards.size()) {
            throw new IllegalStateException("There exists more than one assignment guard in this block.");
        }
        for (final JumpInsn jumpInsn : supposedAssignmentGuards) {
            return jumpInsn;
        }
        return NullJumpInsn.getInstance();
    }

    private Collection<JumpInsn> collectSupposedAssignmentGuards() {
        final Set<JumpInsn> result = new HashSet<JumpInsn>();
        for (final JumpInsn jumpInsn : collectAllConditionCheckInstructionsOfBlock()) {
            final AssignmentGuard.Builder builder = new AssignmentGuard.Builder(jumpInsn);
            final JumpInsn possibleAssignmentGuard = getAssignmentGuard(jumpInsn.getIndexWithinBlock(), builder);
            if (possibleAssignmentGuard.isAssignmentGuard()) {
                result.add(possibleAssignmentGuard);
            }
        }
        return result;
    }

    private Collection<JumpInsn> collectAllConditionCheckInstructionsOfBlock() {
        final ArrayList<JumpInsn> result = new ArrayList<JumpInsn>();
        int indexWithinBlock = 0;
        for (final AbstractInsnNode insn : controlFlowBlock.getBlockInstructions()) {
            if (isConditionCheckInstruction(insn)) {
                final JumpInsnNode jumpInsnNode = (JumpInsnNode) insn;
                final int indexWithinMethod = controlFlowBlock.getIndexWithinMethod(indexWithinBlock);
                result.add(DefaultJumpInsn.newInstance(jumpInsnNode, indexWithinBlock, indexWithinMethod));
            }
            indexWithinBlock++;
        }
        result.trimToSize();
        return result;
    }

    private static boolean isConditionCheckInstruction(final AbstractInsnNode insn) {
        final int opcode = insn.getOpcode();
        return AbstractInsnNode.JUMP_INSN == insn.getType() && opcode != Opcodes.GOTO && opcode != Opcodes.JSR
                && opcode != Opcodes.RET;
    }

    private JumpInsn getAssignmentGuard(final int indexOfInstructionToAnalyse,
            final AssignmentGuard.Builder builder) {
        final JumpInsn result;
        final List<AbstractInsnNode> blockInstructions = controlFlowBlock.getBlockInstructions();
        final int indexOfPredecessorInstruction = indexOfInstructionToAnalyse - 1;
        final AbstractInsnNode predecessorInstruction = blockInstructions.get(indexOfPredecessorInstruction);
        builder.addPredecessorInstruction(predecessorInstruction);
        if (isGetfieldOrGetstaticForCandidate(predecessorInstruction)) {
            result = builder.build();
        } else if (isLoadInstructionForAlias(predecessorInstruction)) {
            result = builder.build();
        } else if (isEqualsInstruction(predecessorInstruction)) {
            result = NullJumpInsn.getInstance();
        } else if (isPushNullOntoStackInstruction(predecessorInstruction)) {
            result = getAssignmentGuard(indexOfPredecessorInstruction, builder);
        } else if (isComparisonInstruction(predecessorInstruction)) {
            result = getAssignmentGuard(indexOfPredecessorInstruction, builder);
        } else {
            result = NullJumpInsn.getInstance();
        }
        return result;
    }

    private boolean isGetfieldOrGetstaticForCandidate(final AbstractInsnNode insn) {
        boolean result = false;
        if (GETFIELD == insn.getOpcode() || GETSTATIC == insn.getOpcode()) {
            final FieldInsnNode getInstruction = (FieldInsnNode) insn;
            result = candidateName.equals(getInstruction.name);
        }
        return result;
    }

    private boolean isLoadInstructionForAlias(final AbstractInsnNode insn) {
        final Finder<Alias> f = AliasFinder.newInstance(candidateName, controlFlowBlock);
        final Alias alias = f.find();
        return alias.doesExist && isLoadInstructionForAlias(insn, alias);
    }

    private static boolean isLoadInstructionForAlias(final AbstractInsnNode insn, final Alias alias) {
        boolean result = false;
        if (AbstractInsnNode.VAR_INSN == insn.getType()) {
            final VarInsnNode loadInstruction = (VarInsnNode) insn;
            result = loadInstruction.var == alias.localVariable;
        }
        return result;
    }

    private static boolean isEqualsInstruction(final AbstractInsnNode insn) {
        final boolean result;
        if (AbstractInsnNode.METHOD_INSN == insn.getType()) {
            final MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
            result = methodInsnNode.name.equals("equals");
        } else {
            result = false;
        }
        return result;
    }

    private static boolean isPushNullOntoStackInstruction(final AbstractInsnNode insn) {
        return ACONST_NULL == insn.getOpcode();
    }

    private static boolean isComparisonInstruction(final AbstractInsnNode insn) {
        switch (insn.getOpcode()) {
        case LCMP:
        case FCMPL:
        case FCMPG:
        case DCMPL:
        case DCMPG:
        case IF_ICMPEQ:
        case IF_ICMPNE:
        case IF_ICMPLT:
        case IF_ICMPGE:
        case IF_ICMPGT:
        case IF_ICMPLE:
        case IF_ACMPEQ:
        case IF_ACMPNE:
            return true;
        default:
            return false;
        }
    }

    @Override
    public String toString() {
        final StringBuilder b = new StringBuilder();
        b.append(getClass().getSimpleName()).append(" [candidateName=").append(candidateName);
        b.append(", controlFlowBlock=").append(controlFlowBlock).append("]");
        return b.toString();
    }

}