/** * */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.checkNotNull; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; /** * @author Juergen Fickel (jufickel@htwg-konstanz.de) * @version 15.02.2013 */ @NotThreadSafe final class EffectiveAssignmentInsnFinder implements Finder<AssignmentInsn> { private final FieldNode targetVariable; private final Collection<ControlFlowBlock> controlFlowBlocks; private EffectiveAssignmentInsnFinder(final FieldNode theTargetVariable, final Collection<ControlFlowBlock> theControlFlowBlocks) { targetVariable = theTargetVariable; controlFlowBlocks = Collections.unmodifiableCollection(theControlFlowBlocks); } /** * Static factory method. * * @param targetVariable * the variable to find the effective {@code putfield} or * {@code putstatic} instruction for. * @param controlFlowBlocks * all control flow blocks of an initialising constructor or * method. * @return a new instance of this class. */ public static EffectiveAssignmentInsnFinder newInstance(final FieldNode targetVariable, final Collection<ControlFlowBlock> controlFlowBlocks) { return new EffectiveAssignmentInsnFinder(checkNotNull(targetVariable), checkNotNull(controlFlowBlocks)); } /** * @return an instance of {@link AssignmentInsn} which contains the * particular {@code putfield} or {@code putstatic} instruction * which effectively sets the value for the provided targetVariable. * The result ist never {@code null} instead {@code isNull} should * be invoked. */ @Override public AssignmentInsn find() { final Collection<AssignmentInsn> assignmentInstructions = findAssignmentInstructionsForVariable(); return getEffectiveAssignmentInstruction(assignmentInstructions); } private Collection<AssignmentInsn> findAssignmentInstructionsForVariable() { final Set<AssignmentInsn> result = new HashSet<AssignmentInsn>(); for (final ControlFlowBlock controlFlowBlock : controlFlowBlocks) { result.addAll(findInAllBlockInstructions(controlFlowBlock)); } return result; } private Collection<AssignmentInsn> findInAllBlockInstructions(final ControlFlowBlock controlFlowBlock) { final Set<AssignmentInsn> result = new HashSet<AssignmentInsn>(); final List<AbstractInsnNode> blockInstructions = controlFlowBlock.getBlockInstructions(); for (int i = 0; i < blockInstructions.size(); i++) { final AbstractInsnNode insn = blockInstructions.get(i); if (isInitialiserForTargetVariable(insn)) { final FieldInsnNode assignmentInsnNode = (FieldInsnNode) insn; final int indexWithinMethod = controlFlowBlock.getIndexWithinMethod(i); result.add(DefaultAssignmentInsn.newInstance(controlFlowBlock, indexWithinMethod, assignmentInsnNode)); } } return result; } private boolean isInitialiserForTargetVariable(final AbstractInsnNode insn) { final boolean result; if (isPutfieldInstruction(insn) || isPutstaticInstruction(insn)) { final FieldInsnNode assignmentInsn = (FieldInsnNode) insn; result = assignmentInsn.name.equals(targetVariable.name); } else { result = false; } return result; } private static boolean isPutfieldInstruction(final AbstractInsnNode insn) { return Opcodes.PUTFIELD == insn.getOpcode(); } private static boolean isPutstaticInstruction(final AbstractInsnNode insn) { return Opcodes.PUTSTATIC == insn.getOpcode(); } /* * The effective assignment instruction is the last one in the sequence of * instructions which puts a value to the target variable. Thus the highest * instruction number indicates the position of the effective assignment * instruction. */ private AssignmentInsn getEffectiveAssignmentInstruction(final Collection<AssignmentInsn> assignmentInstructions) { AssignmentInsn result = NullAssignmentInsn.getInstance(); int maxInstructionNumber = -1; for (final AssignmentInsn assignmentInsn : assignmentInstructions) { final int indexOfAssignmentInstruction = assignmentInsn.getIndexWithinMethod(); if (indexOfAssignmentInstruction > maxInstructionNumber) { maxInstructionNumber = indexOfAssignmentInstruction; result = assignmentInsn; } } return result; } @Override public String toString() { final StringBuilder b = new StringBuilder(); b.append(getClass().getSimpleName()).append(" [targetVariable=").append(targetVariable.name); b.append(", controlFlowBlocks=").append(controlFlowBlocks).append(']'); return b.toString(); } }