package alterrs.jbct.deob;

import static alterrs.asm.Opcodes.ALOAD;
import static alterrs.asm.Opcodes.ASTORE;
import static alterrs.asm.Opcodes.DUP;
import static alterrs.asm.Opcodes.MONITORENTER;

import org.slf4j.Logger;

import alterrs.asm.tree.AbstractInsnNode;
import alterrs.asm.tree.ClassNode;
import alterrs.asm.tree.MethodNode;
import alterrs.asm.tree.VarInsnNode;
import alterrs.commons.LoggingUtil;
import alterrs.jbct.utility.ASMUtil;
import alterrs.jbct.utility.AbstractTransformer;

/**
 * @author Lazaro Brito
 */
public class MonitorDeobfuscator extends AbstractTransformer {
	private static final Logger logger = LoggingUtil.log();

	private int count = 0;

	public MonitorDeobfuscator() {
		super(true);
	}

	@Override
	public void visitInsn(ClassNode c, MethodNode m, AbstractInsnNode n) {
		if (n.getOpcode() == MONITORENTER) {
			AbstractInsnNode astore = ASMUtil.previousRealInsn(n.getPrevious());
			if (astore == null) {
				return;
			}

			AbstractInsnNode dup = ASMUtil.previousRealInsn(astore
					.getPrevious());
			if (dup == null) {
				return;
			}

			if (astore.getOpcode() == ASTORE && dup.getOpcode() == DUP) {
				m.instructions.remove(astore);
				m.instructions.set(dup, astore);

				m.instructions.insert(astore, new VarInsnNode(ALOAD,
						((VarInsnNode) astore).var));

				count++;
			}
		}
	}

	@Override
	public void onFinish() {
		logger.info("Repaired " + count + " monitor blocks!");
	}
}
