<commit_before>package soot.toDex;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.builder.BuilderInstruction;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.writer.builder.BuilderFieldReference;
import org.jf.dexlib2.writer.builder.DexBuilder;

import soot.ArrayType;
import soot.Local;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.BreakpointStmt;
import soot.jimple.CaughtExceptionRef;
import soot.jimple.ConcreteRef;
import soot.jimple.Constant;
import soot.jimple.EnterMonitorStmt;
import soot.jimple.ExitMonitorStmt;
import soot.jimple.GotoStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.IfStmt;
import soot.jimple.InstanceFieldRef;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.MonitorStmt;
import soot.jimple.NopStmt;
import soot.jimple.ParameterRef;
import soot.jimple.RetStmt;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.jimple.StaticFieldRef;
import soot.jimple.Stmt;
import soot.jimple.StmtSwitch;
import soot.jimple.TableSwitchStmt;
import soot.jimple.ThisRef;
import soot.jimple.ThrowStmt;
import soot.toDex.instructions.AddressInsn;
import soot.toDex.instructions.Insn;
import soot.toDex.instructions.Insn10t;
import soot.toDex.instructions.Insn10x;
import soot.toDex.instructions.Insn11x;
import soot.toDex.instructions.Insn12x;
import soot.toDex.instructions.Insn21c;
import soot.toDex.instructions.Insn22c;
import soot.toDex.instructions.Insn22x;
import soot.toDex.instructions.Insn23x;
import soot.toDex.instructions.Insn31t;
import soot.toDex.instructions.Insn32x;
import soot.toDex.instructions.PackedSwitchPayload;
import soot.toDex.instructions.SparseSwitchPayload;
import soot.toDex.instructions.SwitchPayload;
import soot.util.Switchable;

/**
 * A visitor that builds a list of instructions from the Jimple statements it visits.<br>
 * <br>
 * Use {@link Switchable#apply(soot.util.Switch)} with this visitor to add statements
 * and {@link #getFinalInsns()} to get the final dexlib instructions.<br>
 * <br>
 * These final instructions do have correct offsets, jump targets and register numbers.
 * 
 * @see Insn intermediate representation of an instruction
 * @see Instruction final representation of an instruction
 */
public class StmtVisitor implements StmtSwitch {
	
	private static final Map<Opcode, Opcode> oppositeIfs;
	
	static {
		oppositeIfs = new HashMap<Opcode, Opcode>();
		
		oppositeIfs.put(Opcode.IF_EQ, Opcode.IF_NE);
		oppositeIfs.put(Opcode.IF_NE, Opcode.IF_EQ);
		oppositeIfs.put(Opcode.IF_EQZ, Opcode.IF_NEZ);
		oppositeIfs.put(Opcode.IF_NEZ, Opcode.IF_EQZ);
		
		oppositeIfs.put(Opcode.IF_GT, Opcode.IF_LE);
		oppositeIfs.put(Opcode.IF_LE, Opcode.IF_GT);
		oppositeIfs.put(Opcode.IF_GTZ, Opcode.IF_LEZ);
		oppositeIfs.put(Opcode.IF_LEZ, Opcode.IF_GTZ);
		
		oppositeIfs.put(Opcode.IF_GE, Opcode.IF_LT);
		oppositeIfs.put(Opcode.IF_LT, Opcode.IF_GE);
		oppositeIfs.put(Opcode.IF_GEZ, Opcode.IF_LTZ);
		oppositeIfs.put(Opcode.IF_LTZ, Opcode.IF_GEZ);
	}
	
	private final SootMethod belongingMethod;
	private final DexBuilder belongingFile;
	
	private ConstantVisitor constantV;
	
	private RegisterAllocator regAlloc;
	
	private ExprVisitor exprV;
	
	private String lastReturnTypeDescriptor;
	
	private List<Insn> insns;
	
	private List<SwitchPayload> switchPayloads;
	
    // maps used to map Jimple statements to dalvik instructions
    private Map<Insn, Stmt> insnStmtMap = new HashMap<Insn, Stmt>();
    private Map<Instruction, Stmt> instructionStmtMap = new IdentityHashMap<Instruction, Stmt>();
    private Map<Instruction, SwitchPayload> instructionPayloadMap = new IdentityHashMap<Instruction, SwitchPayload>();
    
    public StmtVisitor(SootMethod belongingMethod, DexBuilder belongingFile) {
		this.belongingMethod = belongingMethod;
		this.belongingFile = belongingFile;
		constantV = new ConstantVisitor(belongingFile, this);
		regAlloc = new RegisterAllocator();
		exprV = new ExprVisitor(this, constantV, regAlloc, belongingFile);
		insns = new ArrayList<Insn>();
		switchPayloads = new ArrayList<SwitchPayload>();
	}
	
	protected void setLastReturnTypeDescriptor(String typeDescriptor) {
		lastReturnTypeDescriptor = typeDescriptor;
	}
	
	protected DexBuilder getBelongingFile() {
		return belongingFile;
	}
	
	protected SootClass getBelongingClass() {
		return belongingMethod.getDeclaringClass();
	}
	
    public Map<Instruction, Stmt> getInstructionStmtMap() {
        return this.instructionStmtMap;
    }

    public Map<Instruction, SwitchPayload> getInstructionPayloadMap() {
        return this.instructionPayloadMap;
    }

    protected void addInsn(Insn insn, Stmt s) {
		int highestIndex = insns.size();
		addInsn(highestIndex, insn);
		if (s != null)
			if (insnStmtMap.put(insn, s) != null)
				throw new RuntimeException("Duplicate instruction");
	}
	
	private void addInsn(int positionInList, Insn insn) {
		insns.add(positionInList, insn);
	}
	
	protected void beginNewStmt(Stmt s) {
        addInsn(new AddressInsn(s), null);
	}
	
	public void finalizeInstructions() {
		addSwitchPayloads();
		finishRegs();
	}
	
	private void addSwitchPayloads() {
		// add switch payloads to the end of the insns
		for (SwitchPayload payload : switchPayloads) {
            addInsn(new AddressInsn(payload), null);
            addInsn(payload, null);
		}
	}

	public List<BuilderInstruction> getRealInsns(LabelAssigner labelAssigner) {
		List<BuilderInstruction> finalInsns = new ArrayList<BuilderInstruction>();
		for (Insn i : insns) {
			if (i instanceof AddressInsn) {
				continue; // skip non-insns
			}
			BuilderInstruction realInsn = i.getRealInsn(labelAssigner);
			finalInsns.add(realInsn);
            if (insnStmtMap.containsKey(i)) { // get tags
                instructionStmtMap.put(realInsn, insnStmtMap.get(i));<commit_msg>added a function to reduce unnecessary register copies when writing out dex code<commit_after>package soot.toDex;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.builder.BuilderInstruction;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.writer.builder.BuilderFieldReference;
import org.jf.dexlib2.writer.builder.DexBuilder;

import soot.ArrayType;
import soot.Local;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.BreakpointStmt;
import soot.jimple.CaughtExceptionRef;
import soot.jimple.ConcreteRef;
import soot.jimple.Constant;
import soot.jimple.EnterMonitorStmt;
import soot.jimple.ExitMonitorStmt;
import soot.jimple.GotoStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.IfStmt;
import soot.jimple.InstanceFieldRef;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.MonitorStmt;
import soot.jimple.NopStmt;
import soot.jimple.ParameterRef;
import soot.jimple.RetStmt;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.jimple.StaticFieldRef;
import soot.jimple.Stmt;
import soot.jimple.StmtSwitch;
import soot.jimple.TableSwitchStmt;
import soot.jimple.ThisRef;
import soot.jimple.ThrowStmt;
import soot.toDex.instructions.AddressInsn;
import soot.toDex.instructions.Insn;
import soot.toDex.instructions.Insn10t;
import soot.toDex.instructions.Insn10x;
import soot.toDex.instructions.Insn11x;
import soot.toDex.instructions.Insn12x;
import soot.toDex.instructions.Insn21c;
import soot.toDex.instructions.Insn22c;
import soot.toDex.instructions.Insn22x;
import soot.toDex.instructions.Insn23x;
import soot.toDex.instructions.Insn31t;
import soot.toDex.instructions.Insn32x;
import soot.toDex.instructions.InsnWithOffset;
import soot.toDex.instructions.PackedSwitchPayload;
import soot.toDex.instructions.SparseSwitchPayload;
import soot.toDex.instructions.SwitchPayload;
import soot.util.Switchable;

/**
 * A visitor that builds a list of instructions from the Jimple statements it visits.<br>
 * <br>
 * Use {@link Switchable#apply(soot.util.Switch)} with this visitor to add statements
 * and {@link #getFinalInsns()} to get the final dexlib instructions.<br>
 * <br>
 * These final instructions do have correct offsets, jump targets and register numbers.
 * 
 * @see Insn intermediate representation of an instruction
 * @see Instruction final representation of an instruction
 */
public class StmtVisitor implements StmtSwitch {
	
	private static final Map<Opcode, Opcode> oppositeIfs;
	
	static {
		oppositeIfs = new HashMap<Opcode, Opcode>();
		
		oppositeIfs.put(Opcode.IF_EQ, Opcode.IF_NE);
		oppositeIfs.put(Opcode.IF_NE, Opcode.IF_EQ);
		oppositeIfs.put(Opcode.IF_EQZ, Opcode.IF_NEZ);
		oppositeIfs.put(Opcode.IF_NEZ, Opcode.IF_EQZ);
		
		oppositeIfs.put(Opcode.IF_GT, Opcode.IF_LE);
		oppositeIfs.put(Opcode.IF_LE, Opcode.IF_GT);
		oppositeIfs.put(Opcode.IF_GTZ, Opcode.IF_LEZ);
		oppositeIfs.put(Opcode.IF_LEZ, Opcode.IF_GTZ);
		
		oppositeIfs.put(Opcode.IF_GE, Opcode.IF_LT);
		oppositeIfs.put(Opcode.IF_LT, Opcode.IF_GE);
		oppositeIfs.put(Opcode.IF_GEZ, Opcode.IF_LTZ);
		oppositeIfs.put(Opcode.IF_LTZ, Opcode.IF_GEZ);
	}
	
	private final SootMethod belongingMethod;
	private final DexBuilder belongingFile;
	
	private ConstantVisitor constantV;
	
	private RegisterAllocator regAlloc;
	
	private ExprVisitor exprV;
	
	private String lastReturnTypeDescriptor;
	
	private List<Insn> insns;
	
	private List<SwitchPayload> switchPayloads;
	
    // maps used to map Jimple statements to dalvik instructions
    private Map<Insn, Stmt> insnStmtMap = new HashMap<Insn, Stmt>();
    private Map<Instruction, Stmt> instructionStmtMap = new IdentityHashMap<Instruction, Stmt>();
    private Map<Instruction, SwitchPayload> instructionPayloadMap = new IdentityHashMap<Instruction, SwitchPayload>();
    
    public StmtVisitor(SootMethod belongingMethod, DexBuilder belongingFile) {
		this.belongingMethod = belongingMethod;
		this.belongingFile = belongingFile;
		constantV = new ConstantVisitor(belongingFile, this);
		regAlloc = new RegisterAllocator();
		exprV = new ExprVisitor(this, constantV, regAlloc, belongingFile);
		insns = new ArrayList<Insn>();
		switchPayloads = new ArrayList<SwitchPayload>();
	}
	
	protected void setLastReturnTypeDescriptor(String typeDescriptor) {
		lastReturnTypeDescriptor = typeDescriptor;
	}
	
	protected DexBuilder getBelongingFile() {
		return belongingFile;
	}
	
	protected SootClass getBelongingClass() {
		return belongingMethod.getDeclaringClass();
	}
	
    public Map<Instruction, Stmt> getInstructionStmtMap() {
        return this.instructionStmtMap;
    }

    public Map<Instruction, SwitchPayload> getInstructionPayloadMap() {
        return this.instructionPayloadMap;
    }

    protected void addInsn(Insn insn, Stmt s) {
		int highestIndex = insns.size();
		addInsn(highestIndex, insn);
		if (s != null)
			if (insnStmtMap.put(insn, s) != null)
				throw new RuntimeException("Duplicate instruction");
	}
	
	private void addInsn(int positionInList, Insn insn) {
		insns.add(positionInList, insn);
	}
	
	protected void beginNewStmt(Stmt s) {
        addInsn(new AddressInsn(s), null);
	}
	
	public void finalizeInstructions() {
		addSwitchPayloads();
		finishRegs();
		reduceInstructions();
	}
	
	/**
	 * Reduces the instruction list by removing unnecessary instruction pairs
	 * such as move v0 v1; move v1 v0;
	 */
	private void reduceInstructions() {
		for (int i = 0; i < this.insns.size() - 1; i++) {
			Insn curInsn = this.insns.get(i);
			// Only consider real instructions
			if (curInsn instanceof AddressInsn)
				continue;
			if (!curInsn.getOpcode().name.startsWith("move/"))
				continue;
			
			// Skip over following address instructions
			Insn nextInsn = null;
			int nextIndex = -1;
			for (int j = i + 1; j < this.insns.size(); j++) {
				Insn candidate = this.insns.get(j);
				if (candidate instanceof AddressInsn)
					continue;
				nextInsn = candidate;
				nextIndex = j;
				break;
			}
			if (nextInsn == null || !nextInsn.getOpcode().name.startsWith("move/"))
				continue;
			
			// Do not remove the last instruction in the body as we need to remap
			// jump targets to the successor
			if (nextIndex == this.insns.size() - 1)
				continue;
			
			// Check if we have a <- b; b <- a;
			Register firstTarget = curInsn.getRegs().get(0);
			Register firstSource = curInsn.getRegs().get(1);
			Register secondTarget = nextInsn.getRegs().get(0);
			Register secondSource = nextInsn.getRegs().get(1);
			if (firstTarget.equals(secondSource) && secondTarget.equals(firstSource)) {
				Stmt origStmt = insnStmtMap.get(nextInsn);
				
				// Remove the second instruction as it does not change any
				// state. We cannot remove the first instruction as other
				// instructions may depend on the register being set.
				if (origStmt == null || !isJumpTarget(origStmt)) {
					insns.remove(nextIndex);
				
					if (origStmt != null) {
						insnStmtMap.remove(nextInsn);
						insnStmtMap.put(this.insns.get(nextIndex + 1), origStmt);
					}
				}
			}
		}
	}
	
	private boolean isJumpTarget(Stmt target) {
		for (Insn insn : this.insns)
			if (insn instanceof InsnWithOffset)
				if (((InsnWithOffset) insn).getTarget() == target)
					return true;
		return false;
	}

	private void addSwitchPayloads() {
		// add switch payloads to the end of the insns
		for (SwitchPayload payload : switchPayloads) {
            addInsn(new AddressInsn(payload), null);
            addInsn(payload, null);
		}
	}

	public List<BuilderInstruction> getRealInsns(LabelAssigner labelAssigner) {
		List<BuilderInstruction> finalInsns = new ArrayList<BuilderInstruction>();
		for (Insn i : insns) {
			if (i instanceof AddressInsn) {
				continue; // skip non-insns
			}
			BuilderInstruction realInsn = i.getRealInsn(labelAssigner);
			finalInsns.add(realInsn);
            if (insnStmtMap.containsKey(i)) { // get tags
                instructionStmtMap.put(realInsn, insnStmtMap.get(i));
{'fraction_non_alphanumeric': 0.10032962505150392, 'fraction_numerical': 0.004395000686718858, 'mean_word_length': 4.695346108721157, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5426921', 'n_tokens_mistral': 5193, 'n_tokens_neox': 4781, 'n_words': 1068}