/**
*
*/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.tree.AbstractInsnNode.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import org.objectweb.asm.tree.*;
/**
* An Assignment Guard is the condition check which decides whether or
* not to initialise a certain lazy variable.
*
* @author Juergen Fickel (jufickel@htwg-konstanz.de)
* @version 07.03.2013
*/
final class AssignmentGuard implements JumpInsn {
/**
* Builder for instances of {@link AssignmentGuard}.
*/
@NotThreadSafe
public static final class Builder {
private final JumpInsn jumpInstruction;
private final ArrayList<AbstractInsnNode> predecessorInstructions;
public Builder(final JumpInsn theJumpInstruction) {
jumpInstruction = theJumpInstruction;
predecessorInstructions = new ArrayList<AbstractInsnNode>();
}
public void addPredecessorInstruction(final AbstractInsnNode predecessor) {
predecessorInstructions.add(predecessor);
}
public AssignmentGuard build() {
predecessorInstructions.trimToSize();
return AssignmentGuard.newInstance(jumpInstruction, predecessorInstructions);
}
@Override
public String toString() {
final StringBuilder b = new StringBuilder();
b.append(getDeclaringClassPrefix()).append(getClass().getSimpleName());
b.append(" [jumpInstruction=").append(jumpInstruction);
b.append(", predecessorInstructions=").append(predecessorInstructions).append(']');
return b.toString();
}
private String getDeclaringClassPrefix() {
final String result;
final Class<?> superclass = getClass().getDeclaringClass();
if (null != superclass) {
final StringBuilder b = new StringBuilder();
b.append(superclass.getSimpleName()).append('.');
result = b.toString();
} else {
result = "";
}
return result;
}
} // class AssignmentGuardBuilder
@Immutable
private static final class InstructionNodesHashCodeCalculator {
private static final byte INITIAL_RESULT = 1;
private final int prime;
public InstructionNodesHashCodeCalculator(final int thePrime) {
prime = thePrime;
}
public int hashCode(final AbstractInsnNode insn) {
final int result;
final int nodeType = insn.getType();
if (FIELD_INSN == nodeType) {
result = hashCode((FieldInsnNode) insn);
} else if (VAR_INSN == nodeType) {
result = hashCode((VarInsnNode) insn);
} else if (INSN == nodeType) {
result = hashCode((InsnNode) insn);
} else if (INT_INSN == nodeType) {
result = hashCode((IntInsnNode) insn);
} else if (INVOKE_DYNAMIC_INSN == nodeType) {
result = hashCode((InvokeDynamicInsnNode) insn);
} else if (JUMP_INSN == nodeType) {
result = hashCode((JumpInsnNode) insn);
} else if (LABEL == nodeType) {
result = hashCode((LabelNode) insn);
} else if (LDC_INSN == nodeType) {
result = hashCode((LdcInsnNode) insn);
} else if (LINE == nodeType) {
result = hashCode((LineNumberNode) insn);
} else if (METHOD_INSN == nodeType) {
result = hashCode((MethodInsnNode) insn);
} else if (IINC_INSN == nodeType) {
result = hashCode((IincInsnNode) insn);
} else if (LOOKUPSWITCH_INSN == nodeType) {
result = hashCode((LookupSwitchInsnNode) insn);
} else if (FRAME == nodeType) {
result = hashCode((FrameNode) insn);
} else if (TYPE_INSN == nodeType) {
result = hashCode((TypeInsnNode) insn);
} else if (MULTIANEWARRAY_INSN == nodeType) {
result = hashCode((MultiANewArrayInsnNode) insn);
} else if (TABLESWITCH_INSN == nodeType) {
result = hashCode((TableSwitchInsnNode) insn);
} else {
result = insn.hashCode();
}
return hashCode(result, insn.getOpcode());
}
private int hashCode(final FieldInsnNode insn) {
int result = INITIAL_RESULT;
result = hashCode(result, insn.desc.hashCode());
result = hashCode(result, insn.name.hashCode());
result = hashCode(result, insn.owner.hashCode());
return result;
}
private final int hashCode(final int preliminaryResult, final int hashCode) {
return prime * preliminaryResult + hashCode;
}
private int hashCode(final VarInsnNode insn) {
return hashCode(INITIAL_RESULT, insn.var);
}
private int hashCode(final InsnNode insn) {
return hashCode(INITIAL_RESULT, insn.hashCode());
}
private int hashCode(final IntInsnNode insn) {
return hashCode(INITIAL_RESULT, insn.operand);
}
private int hashCode(final InvokeDynamicInsnNode insn) {
int result = INITIAL_RESULT;
result = hashCode(result, insn.name.hashCode());
result = hashCode(result, insn.desc.hashCode());
result = hashCode(result, insn.bsm.hashCode());
result = hashCode(result, insn.hashCode());
return result;
}
private int hashCode(final JumpInsnNode insn) {
return hashCode(INITIAL_RESULT, 0);
}
private int hashCode(final LabelNode insn) {
return hashCode(INITIAL_RESULT, 0);
}
private int hashCode(final LdcInsnNode insn) {
return hashCode(INITIAL_RESULT, insn.cst.hashCode());
}
private int hashCode(final LineNumberNode insn) {
return hashCode(INITIAL_RESULT, 0);
}
private int hashCode(final MethodInsnNode insn) {
int result = INITIAL_RESULT;
result = hashCode(result, insn.desc.hashCode());
result = hashCode(result, insn.name.hashCode());
result = hashCode(result, insn.owner.hashCode());
return result;
}
private int hashCode(final IincInsnNode insn) {
int result = INITIAL_RESULT;
result = hashCode(result, insn.var);
result = hashCode(result, insn.incr);
return result;
}
private int hashCode(final LookupSwitchInsnNode insn) {
return hashCode(INITIAL_RESULT, insn.keys.hashCode());
}
private int hashCode(final FrameNode insn) {
int result = INITIAL_RESULT;
result = hashCode(result, insn.local.hashCode());
result = hashCode(result, insn.stack.hashCode());
return result;
}
private int hashCode(final TypeInsnNode insn) {
return hashCode(INITIAL_RESULT, insn.desc.hashCode());
}
private int hashCode(final MultiANewArrayInsnNode insn) {
int result = INITIAL_RESULT;
result = hashCode(result, insn.dims);
result = hashCode(result, insn.desc.hashCode());
return result;
}
private int hashCode(final TableSwitchInsnNode insn) {
int result = INITIAL_RESULT;
result = hashCode(result, insn.min);
result = hashCode(result, insn.max);
return result;
}
} // class InstructionNodeHashCodeCalculator
@Immutable
private static final class InstructionNodesComparator {
public boolean equals(final FieldInsnNode f1, final FieldInsnNode f2) {
return f1.desc.equals(f2.desc) && f1.name.equals(f2.name) && f1.owner.equals(f2.owner);
}
public boolean equals(final VarInsnNode insnThis, final VarInsnNode insnOther) {
return insnThis.var == insnOther.var;
}
public boolean equals(final InsnNode insnThis, final InsnNode insnOther) {
return true;
}
public boolean equals(final IntInsnNode insnThis, final IntInsnNode insnOther) {
return insnThis.operand == insnOther.operand;
}
public boolean equals(final InvokeDynamicInsnNode i1, final InvokeDynamicInsnNode i2) {
return i1.name.equals(i2.name) && i1.desc.equals(i2.desc) && i1.bsm.equals(i2.bsm)
&& Arrays.equals(i1.bsmArgs, i2.bsmArgs);
}
public boolean equals(final JumpInsnNode insnThis, final JumpInsnNode insnOther) {
return true;
}
public boolean equals(final LabelNode insnThis, final LabelNode insnOther) {
return true;
}
public boolean equals(final LdcInsnNode insnThis, final LdcInsnNode insnOther) {
return insnThis.cst.equals(insnOther.cst);
}
public boolean equals(final LineNumberNode insnThis, final LineNumberNode insnOther) {
return true;
}
public boolean equals(final MethodInsnNode m1, final MethodInsnNode m2) {
return m1.desc.equals(m2.name) && m1.name.equals(m2.name) && m1.owner.equals(m2.owner);
}
public boolean equals(final IincInsnNode i1, final IincInsnNode i2) {
return i1.var == i2.var && i1.incr == i2.incr;
}
public boolean equals(final LookupSwitchInsnNode l1, final LookupSwitchInsnNode l2) {
return l1.keys.equals(l2.keys);
}
public boolean equals(final FrameNode f1, final FrameNode f2) {
return f1.local.equals(f2.local) && f1.stack.equals(f2.stack);
}
public boolean equals(final TypeInsnNode t1, final TypeInsnNode t2) {
return t1.desc.equals(t2.desc);
}
public boolean equals(final MultiANewArrayInsnNode m1, final MultiANewArrayInsnNode m2) {
return m1.desc.equals(m2.desc) && m1.dims == m2.dims;
}
public boolean equals(final TableSwitchInsnNode t1, final TableSwitchInsnNode t2) {
return t1.min == t2.min && t1.max == t2.max;
}
} // class InstructionNodesComparator
private final JumpInsn delegationTarget;
private final List<AbstractInsnNode> predecessorInstructions;
private AssignmentGuard(final JumpInsn theDelegationTarget,
final List<AbstractInsnNode> thePredecessorInstructions) {
delegationTarget = theDelegationTarget;
predecessorInstructions = thePredecessorInstructions;
}
public static AssignmentGuard newInstance(final JumpInsn delegationTarget,
final List<AbstractInsnNode> predecessorInstructions) {
validateArguments(delegationTarget, predecessorInstructions);
return new AssignmentGuard(delegationTarget, predecessorInstructions);
}
private static void validateArguments(final JumpInsn delegationTarget,
final List<AbstractInsnNode> predecessorInstructions) {
final String msg = "Argument '%s' must not be %s!";
checkNotNull(delegationTarget, msg, "delegationTarget", "null");
checkNotNull(predecessorInstructions, msg, "predecessorInstructions", "null");
checkArgument(!predecessorInstructions.isEmpty(), msg, "predecessorInstructions", "empty");
}
@Override
public JumpInsnNode getJumpInsnNode() {
return delegationTarget.getJumpInsnNode();
}
@Override
public int getIndexWithinBlock() {
return delegationTarget.getIndexWithinBlock();
}
@Override
public int getIndexWithinMethod() {
return delegationTarget.getIndexWithinMethod();
}
@Override
public Opcode getOpcode() {
return delegationTarget.getOpcode();
}
@Override
public boolean isAssignmentGuard() {
return true;
}
@Override
public int compareTo(final JumpInsn o) {
return delegationTarget.compareTo(o);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
final JumpInsnNode jumpInsnNode = getJumpInsnNode();
result = prime * result + jumpInsnNode.getOpcode();
final InstructionNodesHashCodeCalculator hcc = new InstructionNodesHashCodeCalculator(prime);
for (final AbstractInsnNode predecessor : predecessorInstructions) {
final int hashCodeOfPredecessor = hcc.hashCode(predecessor);
result = prime * result + hashCodeOfPredecessor;
}
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof AssignmentGuard)) {
return false;
}
final AssignmentGuard other = (AssignmentGuard) obj;
final JumpInsnNode jumpInsnNodeThis = delegationTarget.getJumpInsnNode();
final JumpInsnNode jumpInsnNodeOther = other.getJumpInsnNode();
if (jumpInsnNodeThis.getOpcode() != jumpInsnNodeOther.getOpcode()) {
return false;
}
if (predecessorInstructions.size() != other.predecessorInstructions.size()) {
return false;
}
final InstructionNodesComparator inc = new InstructionNodesComparator();
for (int i = 0; i < predecessorInstructions.size(); i++) {
final AbstractInsnNode insnsThis = predecessorInstructions.get(i);
final AbstractInsnNode insnsOther = other.predecessorInstructions.get(i);
if (insnsThis.getOpcode() != insnsOther.getOpcode()) {
return false;
}
if (instructionsAreUnequal(insnsThis, insnsOther, inc)) {
return false;
}
}
return true;
}
private static boolean instructionsAreUnequal(final AbstractInsnNode insnThis,
final AbstractInsnNode insnOther,
final InstructionNodesComparator inc) {
final int nodeType = insnThis.getType();
final boolean result;
if (FIELD_INSN == nodeType) {
result = inc.equals((FieldInsnNode) insnThis, (FieldInsnNode) insnOther);
} else if (VAR_INSN == nodeType) {
result = inc.equals((VarInsnNode) insnThis, (VarInsnNode) insnOther);
} else if (INSN == nodeType) {
result = inc.equals((InsnNode) insnThis, (InsnNode) insnOther);
} else if (INT_INSN == nodeType) {
result = inc.equals((IntInsnNode) insnThis, (IntInsnNode) insnOther);
} else if (INVOKE_DYNAMIC_INSN == nodeType) {
result = inc.equals((InvokeDynamicInsnNode) insnThis, (InvokeDynamicInsnNode) insnOther);
} else if (JUMP_INSN == nodeType) {
result = inc.equals((JumpInsnNode) insnThis, (JumpInsnNode) insnOther);
} else if (LABEL == nodeType) {
result = inc.equals((LabelNode) insnThis, (LabelNode) insnOther);
} else if (LDC_INSN == nodeType) {
result = inc.equals((LdcInsnNode) insnThis, (LdcInsnNode) insnOther);
} else if (LINE == nodeType) {
result = inc.equals((LineNumberNode) insnThis, (LineNumberNode) insnOther);
} else if (METHOD_INSN == nodeType) {
result = inc.equals((MethodInsnNode) insnThis, (MethodInsnNode) insnOther);
} else if (IINC_INSN == nodeType) {
result = inc.equals((IincInsnNode) insnThis, (IincInsnNode) insnOther);
} else if (LOOKUPSWITCH_INSN == nodeType) {
result = inc.equals((LookupSwitchInsnNode) insnThis, (LookupSwitchInsnNode) insnOther);
} else if (FRAME == nodeType) {
result = inc.equals((FrameNode) insnThis, (FrameNode) insnOther);
} else if (TYPE_INSN == nodeType) {
result = inc.equals((TypeInsnNode) insnThis, (TypeInsnNode) insnOther);
} else if (MULTIANEWARRAY_INSN == nodeType) {
result = inc.equals((MultiANewArrayInsnNode) insnThis, (MultiANewArrayInsnNode) insnOther);
} else if (TABLESWITCH_INSN == nodeType) {
result = inc.equals((TableSwitchInsnNode) insnThis, (TableSwitchInsnNode) insnOther);
} else {
result = false;
}
return !result;
}
@Override
public String toString() {
final StringBuilder b = new StringBuilder();
b.append(getClass().getSimpleName()).append(" [delegationTarget=").append(delegationTarget);
b.append(", predecessorInstructions=").append(predecessorInstructions).append(']');
return b.toString();
}
}