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

Java tutorial

Introduction

Here is the source code for org.mutabilitydetector.checkers.settermethod.AliasFinder.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.ASTORE;
import static org.objectweb.asm.Opcodes.DSTORE;
import static org.objectweb.asm.Opcodes.FSTORE;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.LSTORE;

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.VarInsnNode;

/**
 * @author Juergen Fickel (jufickel@htwg-konstanz.de)
 * @version 27.02.2013
 */
@Immutable
final class AliasFinder implements Finder<Alias> {

    private final Set<ControlFlowBlock> alreadyVisited;
    private final String variableName;
    private final ControlFlowBlock controlFlowBlockToExamine;

    private AliasFinder(final String theVariableName, final ControlFlowBlock theControlFlowBlockToExamine) {
        alreadyVisited = new HashSet<ControlFlowBlock>();
        variableName = theVariableName;
        controlFlowBlockToExamine = theControlFlowBlockToExamine;
    }

    /**
     * Creates a new instance of this class.
     * 
     * @param variableName
     *            name of the instance variable to search aliases for. Must
     *            neither be {@code null} nor empty.
     * @param controlFlowBlockToExamine
     *            a {@link ControlFlowBlock} which possibly contains the setup
     *            of an alias for a lazy variable. This method thereby examines
     *            predecessors of {@code block}, too. This parameter must not be
     *            {@code null}. 
     * @return a new instance of this class.
     */
    public static AliasFinder newInstance(final String variableName,
            final ControlFlowBlock controlFlowBlockToExamine) {
        checkArgument(!variableName.isEmpty());
        return new AliasFinder(variableName, checkNotNull(controlFlowBlockToExamine));
    }

    /**
     * @return an {@link Alias}. This is never {@code null}. If {@code block}
     *         does not contain an alias, the following is being returned:
     *         {@code Alias [doesExist=false, localVariable=Integer.MIN_VALUE]}.
     */
    @Override
    public Alias find() {
        return searchForAliasInBlock(controlFlowBlockToExamine);
    }

    /*
     * Uses method argument as this method works recursively.
     */
    private Alias searchForAliasInBlock(final ControlFlowBlock block) {
        checkNotNull(block);
        Alias result = Alias.newInstance(false, Integer.MIN_VALUE);
        if (alreadyVisited.contains(block)) {
            return result;
        }
        alreadyVisited.add(block);
        final List<AbstractInsnNode> insns = block.getBlockInstructions();
        final int indexOfGetfield = findIndexOfGetfieldForVariable(insns);
        if (indexOfGetfieldFound(indexOfGetfield)) {
            final AbstractInsnNode successorInsnOfGetfieldForVariable = insns.get(indexOfGetfield + 1);
            if (isStoreInstruction(successorInsnOfGetfieldForVariable)) {
                final VarInsnNode storeInstruction = (VarInsnNode) successorInsnOfGetfieldForVariable;
                result = Alias.newInstance(true, storeInstruction.var);
            }
        }
        if (!result.doesExist) {
            for (final ControlFlowBlock predecessor : block.getPredecessors()) {
                return searchForAliasInBlock(predecessor);
            }
        }
        return result;
    }

    private int findIndexOfGetfieldForVariable(final List<AbstractInsnNode> instructions) {
        int result = -1;
        int i = 0;
        for (final AbstractInsnNode instruction : instructions) {
            if (isGetfieldForVariable(instruction)) {
                result = i;
                break;
            }
            i++;
        }
        return result;
    }

    private boolean isGetfieldForVariable(final AbstractInsnNode insn) {
        boolean result = false;
        if (Opcodes.GETFIELD == insn.getOpcode()) {
            final FieldInsnNode getfield = (FieldInsnNode) insn;
            result = variableName.equals(getfield.name);
        }
        return result;
    }

    private static boolean indexOfGetfieldFound(final int index) {
        return -1 < index;
    }

    private static boolean isStoreInstruction(final AbstractInsnNode insn) {
        switch (insn.getOpcode()) {
        case ISTORE:
        case LSTORE:
        case FSTORE:
        case DSTORE:
        case ASTORE:
            return true;
        default:
            return false;
        }
    }

    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("AliasFinder [").append("variableName=").append(variableName);
        builder.append(", controlFlowBlockToExamine=").append(controlFlowBlockToExamine);
        builder.append(", alreadyVisited=").append(alreadyVisited).append("]");
        return builder.toString();
    }

}