package cuchaz.enigma.analysis; import cuchaz.enigma.utils.Utils; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Interpreter; import org.objectweb.asm.tree.analysis.Value; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; public class InterpreterPair extends Interpreter> { private final Interpreter left; private final Interpreter right; public InterpreterPair(Interpreter left, Interpreter right) { super(Utils.ASM_VERSION); this.left = left; this.right = right; } @Override public PairValue newValue(Type type) { return pair( left.newValue(type), right.newValue(type) ); } @Override public PairValue newOperation(AbstractInsnNode insn) throws AnalyzerException { return pair( left.newOperation(insn), right.newOperation(insn) ); } @Override public PairValue copyOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { return pair( left.copyOperation(insn, value.left), right.copyOperation(insn, value.right) ); } @Override public PairValue unaryOperation(AbstractInsnNode insn, PairValue value) throws AnalyzerException { return pair( left.unaryOperation(insn, value.left), right.unaryOperation(insn, value.right) ); } @Override public PairValue binaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2) throws AnalyzerException { return pair( left.binaryOperation(insn, value1.left, value2.left), right.binaryOperation(insn, value1.right, value2.right) ); } @Override public PairValue ternaryOperation(AbstractInsnNode insn, PairValue value1, PairValue value2, PairValue value3) throws AnalyzerException { return pair( left.ternaryOperation(insn, value1.left, value2.left, value3.left), right.ternaryOperation(insn, value1.right, value2.right, value3.right) ); } @Override public PairValue naryOperation(AbstractInsnNode insn, List> values) throws AnalyzerException { return pair( left.naryOperation(insn, values.stream().map(v -> v.left).collect(Collectors.toList())), right.naryOperation(insn, values.stream().map(v -> v.right).collect(Collectors.toList())) ); } @Override public void returnOperation(AbstractInsnNode insn, PairValue value, PairValue expected) throws AnalyzerException { left.returnOperation(insn, value.left, expected.left); right.returnOperation(insn, value.right, expected.right); } @Override public PairValue merge(PairValue value1, PairValue value2) { return pair( left.merge(value1.left, value2.left), right.merge(value1.right, value2.right) ); } private PairValue pair(V left, W right) { if (left == null && right == null) { return null; } return new PairValue<>(left, right); } public static final class PairValue implements Value { public final V left; public final W right; public PairValue(V left, W right) { if (left == null && right == null) { throw new IllegalArgumentException("should use null rather than pair of nulls"); } this.left = left; this.right = right; } @Override public boolean equals(Object o) { return o instanceof InterpreterPair.PairValue && Objects.equals(left, ((PairValue) o).left) && Objects.equals(right, ((PairValue) o).right); } @Override public int hashCode() { return left.hashCode() * 31 + right.hashCode(); } @Override public int getSize() { return (left == null ? right : left).getSize(); } } }