<commit_before>package soot.toDex;

import java.util.ArrayList;
import java.util.List;

import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.Code.Opcode;

import soot.ArrayType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.IntegerType;
import soot.LongType;
import soot.SootClass;
import soot.Type;
import soot.Value;
import soot.jimple.AddExpr;
import soot.jimple.AndExpr;
import soot.jimple.CastExpr;
import soot.jimple.CmpExpr;
import soot.jimple.CmpgExpr;
import soot.jimple.CmplExpr;
import soot.jimple.DivExpr;
import soot.jimple.DynamicInvokeExpr;
import soot.jimple.EqExpr;
import soot.jimple.ExprSwitch;
import soot.jimple.GeExpr;
import soot.jimple.GtExpr;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InstanceOfExpr;
import soot.jimple.IntConstant;
import soot.jimple.InterfaceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.LeExpr;
import soot.jimple.LengthExpr;
import soot.jimple.LtExpr;
import soot.jimple.MulExpr;
import soot.jimple.NeExpr;
import soot.jimple.NegExpr;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.NullConstant;
import soot.jimple.OrExpr;
import soot.jimple.RemExpr;
import soot.jimple.ShlExpr;
import soot.jimple.ShrExpr;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.SubExpr;
import soot.jimple.UshrExpr;
import soot.jimple.VirtualInvokeExpr;
import soot.jimple.XorExpr;
import soot.toDex.instructions.Insn;
import soot.toDex.instructions.Insn11x;
import soot.toDex.instructions.Insn12x;
import soot.toDex.instructions.Insn21c;
import soot.toDex.instructions.Insn21t;
import soot.toDex.instructions.Insn22b;
import soot.toDex.instructions.Insn22c;
import soot.toDex.instructions.Insn22s;
import soot.toDex.instructions.Insn22t;
import soot.toDex.instructions.Insn23x;
import soot.toDex.instructions.Insn35c;
import soot.toDex.instructions.Insn3rc;
import soot.toDex.instructions.InsnWithOffset;
import soot.util.Switchable;

/**
 * A visitor that builds a list of instructions from the Jimple expressions it visits.<br>
 * <br>
 * Use {@link Switchable#apply(soot.util.Switch)} with this visitor to add statements.
 * These are added to the instructions in the {@link StmtVisitor}.<br>
 * If the expression is part of an assignment or an if statement, use {@link #setDestinationReg(Register)}
 * and {@link #setTargetForOffset(Stmt)}, respectively.
 * 
 * @see StmtVisitor
 */
public class ExprVisitor implements ExprSwitch {
	
	private StmtVisitor stmtV;
	
	private ConstantVisitor constantV;
	
	private RegisterAllocator regAlloc;
	
	private Register destinationReg;
	
	private Stmt targetForOffset;
	
	public ExprVisitor(StmtVisitor stmtV, ConstantVisitor constantV, RegisterAllocator regAlloc) {
		this.stmtV = stmtV;
		this.constantV = constantV;
		this.regAlloc = regAlloc;
	}
	
	public void setDestinationReg(Register destinationReg) {
		this.destinationReg = destinationReg;
	}
	
	public void setTargetForOffset(Stmt targetForOffset) {
		this.targetForOffset = targetForOffset;
	}

	public void defaultCase(Object o) {
		// rsub-int and rsub-int/lit8 aren't generated, since we cannot detect there usage in jimple
		throw new Error("unknown Object (" + o.getClass() + ") as Expression: " + o);
	}

	public void caseDynamicInvokeExpr(DynamicInvokeExpr v) {
		throw new Error("DynamicInvokeExpr not supported: " + v);
	}
	
	public void caseSpecialInvokeExpr(SpecialInvokeExpr sie) {
		MethodIdItem method = DexPrinter.toMethodIdItem(sie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> arguments = getInstanceInvokeArgumentRegs(sie);
		if (isCallToConstructor(sie) || isCallToPrivate(sie)) {
			stmtV.addInsn(buildInvokeInsn("invoke-direct", method, arguments));
		} else if (isCallToSuper(sie)) {
			stmtV.addInsn(buildInvokeInsn("invoke-super", method, arguments));
		} else
			throw new Error("unknown SpecialInvokeExpr (no call to constructor, super or private method): " + sie);
	}

	private Insn buildInvokeInsn(String invokeOpcode, MethodIdItem method, List<Register> argumentRegs) {
		Insn invokeInsn;
		int regCountForArguments = SootToDexUtils.getRealRegCount(argumentRegs);
		if (regCountForArguments <= 5) {
			Register[] paddedArray = pad35cRegs(argumentRegs);
			Opcode opc = Opcode.getOpcodeByName(invokeOpcode);
			invokeInsn = new Insn35c(opc, regCountForArguments, paddedArray[0], paddedArray[1], paddedArray[2], paddedArray[3], paddedArray[4], method);
		} else if (regCountForArguments <= 255) {
			Opcode opc = Opcode.getOpcodeByName(invokeOpcode + "/range");
			invokeInsn = new Insn3rc(opc, argumentRegs, (short) regCountForArguments, method);
		} else {
			throw new Error("too many parameter registers for invoke-* (> 255): " + regCountForArguments + " or registers too big (> 4 bits)");
		}
		// save the return type for the move-result insn
		stmtV.setLastReturnTypeDescriptor(method.getPrototype().getReturnType().getTypeDescriptor());
		return invokeInsn;
	}

	private boolean isCallToPrivate(SpecialInvokeExpr sie) {
		return sie.getMethod().isPrivate();
	}

	private boolean isCallToConstructor(SpecialInvokeExpr sie) {
		return sie.getMethod().isConstructor();
	}

	private boolean isCallToSuper(SpecialInvokeExpr sie) {
		SootClass classWithInvokation = sie.getMethod().getDeclaringClass();
		SootClass currentClass = stmtV.getBelongingClass();
		while (currentClass.hasSuperclass()) {
			currentClass = currentClass.getSuperclass();
			if (currentClass.equals(classWithInvokation)) {
				return true;
			}
		}
		return false; // we arrived at java.lang.Object and did not find a declaration
	}

	public void caseVirtualInvokeExpr(VirtualInvokeExpr vie) {
		/*
		 * for final methods we build an invoke-virtual opcode, too, although the dex spec says that a virtual method is not final.
		 * An alternative would be the invoke-direct opcode, but this is inconsistent with dx's output...
		 */
		MethodIdItem method = DexPrinter.toMethodIdItem(vie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> argumentRegs = getInstanceInvokeArgumentRegs(vie);
		stmtV.addInsn(buildInvokeInsn("invoke-virtual", method, argumentRegs));
	}
	
	private List<Register> getInvokeArgumentRegs(InvokeExpr ie) {
		List<Register> argumentRegs = new ArrayList<Register>();
		for (Value arg : ie.getArgs()) {
			Register currentReg = regAlloc.asImmediate(arg, constantV);
			argumentRegs.add(currentReg);
		}
		return argumentRegs;
	}

	private List<Register> getInstanceInvokeArgumentRegs(InstanceInvokeExpr iie) {
		List<Register> argumentRegs = getInvokeArgumentRegs(iie);
		// always add reference to callee as first parameter (instance != static)
		Value callee = iie.getBase();
		Register calleeRegister = regAlloc.asLocal(callee);
		argumentRegs.add(0, calleeRegister);
		return argumentRegs;
	}

	private Register[] pad35cRegs(List<Register> realRegs) {
		// pad real registers to an array of five bytes, as required by the instruction format "35c"
		Register[] paddedArray = new Register[5];
		int nextReg = 0;
		for (Register realReg : realRegs) {
			paddedArray[nextReg] = realReg;
			/*
			 * we include the second half of a wide with an empty reg for the "gap".
			 * this will be made explicit for dexlib later on.
			 */
			if (realReg.isWide()) {
				nextReg++;
				paddedArray[nextReg] = Register.EMPTY_REGISTER;
			}
			nextReg++;
		}
		// do the real padding with empty regs
		for (; nextReg < 5; nextReg++) {
			paddedArray[nextReg] = Register.EMPTY_REGISTER;
		}
		return paddedArray;
	}

	public void caseInterfaceInvokeExpr(InterfaceInvokeExpr iie) {
		MethodIdItem method = DexPrinter.toMethodIdItem(iie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> arguments = getInstanceInvokeArgumentRegs(iie);
		stmtV.addInsn(buildInvokeInsn("invoke-interface", method, arguments));
	}

	public void caseStaticInvokeExpr(StaticInvokeExpr sie) {
		MethodIdItem method = DexPrinter.toMethodIdItem(sie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> arguments = getInvokeArgumentRegs(sie);
		stmtV.addInsn(buildInvokeInsn("invoke-static", method, arguments));
	}
	
	private Insn buildCalculatingBinaryInsn(String binaryOperation, Value firstOperand, Value secondOperand) {
		/*
		 * it is assumed (but not enforced!) that the types of firstOperand, secondOperand and destinationRegister are compatible
		 */
		Register firstOpReg = regAlloc.asImmediate(firstOperand, constantV);
		
		// use special "/lit"-opcodes if int is the destination's type, the second op is an integer literal and the opc is not sub (no sub*/lit opc available)
		if (destinationReg.getType() instanceof IntType && secondOperand instanceof IntConstant && !binaryOperation.equals("sub")) {
			int secondOpConstant = ((IntConstant) secondOperand).value;
			if (SootToDexUtils.fitsSigned8(secondOpConstant)) {
				return buildLit8BinaryInsn(binaryOperation, firstOpReg, (byte) secondOpConstant);
			}
			if (SootToDexUtils.fitsSigned16(secondOpConstant)) {
				if (!binaryOperation.equals("shl") && !binaryOperation.equals("shr") && !binaryOperation.equals("ushr")) {
					// no shift opc available for /lit16
					return buildLit16BinaryInsn(binaryOperation, firstOpReg, (short) secondOpConstant);
				}
			}
			// constant is too big, so use it as second op and build normally
		}
		Register secondOpReg = regAlloc.asImmediate(secondOperand, constantV);
		// use special "/2addr"-opcodes if destination equals first op
		if (destinationReg.getNumber() == firstOpReg.getNumber()) {
			return build2AddrBinaryInsn(binaryOperation, secondOpReg);
		}
		// no optimized binary opcode possible
		return buildNormalBinaryInsn(binaryOperation, firstOpReg, secondOpReg);
	}

	private String fixIntTypeString(String typeString) {
		if (typeString.equals("boolean") || typeString.equals("byte") || typeString.equals("char") || typeString.equals("short")) {
			return "int";
		}
		return typeString;
	}

	private Insn build2AddrBinaryInsn(String binaryOperation, Register secondOpReg) {
		String localTypeString = destinationReg.getTypeString();
		localTypeString = fixIntTypeString(localTypeString);
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-" + localTypeString + "/2addr");
		return new Insn12x(opc, destinationReg, secondOpReg);
	}

	private Insn buildNormalBinaryInsn(String binaryOperation, Register firstOpReg, Register secondOpReg) {
		String localTypeString = firstOpReg.getTypeString();
		localTypeString = fixIntTypeString(localTypeString);
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-" + localTypeString);
		return new Insn23x(opc, destinationReg, firstOpReg, secondOpReg);
	}
	
	private Insn buildLit16BinaryInsn(String binaryOperation, Register firstOpReg, short secondOpLieteral) {
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-int/lit16");
		return new Insn22s(opc, destinationReg, firstOpReg, secondOpLieteral);
	}

	private Insn buildLit8BinaryInsn(String binaryOperation, Register firstOpReg, byte secondOpLiteral) {
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-int/lit8");
		return new Insn22b(opc, destinationReg, firstOpReg, secondOpLiteral);
	}

	public void caseAddExpr(AddExpr ae) {
		stmtV.addInsn(buildCalculatingBinaryInsn("add", ae.getOp1(), ae.getOp2()));
	}
	
	public void caseSubExpr(SubExpr se) {
		stmtV.addInsn(buildCalculatingBinaryInsn("sub", se.getOp1(), se.getOp2()));
	}
	
	public void caseMulExpr(MulExpr me) {
		stmtV.addInsn(buildCalculatingBinaryInsn("mul", me.getOp1(), me.getOp2()));
	}
	
	public void caseDivExpr(DivExpr de) {
		stmtV.addInsn(buildCalculatingBinaryInsn("div", de.getOp1(), de.getOp2()));
	}
	
	public void caseRemExpr(RemExpr re) {
		stmtV.addInsn(buildCalculatingBinaryInsn("rem", re.getOp1(), re.getOp2()));
	}
	
	public void caseAndExpr(AndExpr ae) {
		stmtV.addInsn(buildCalculatingBinaryInsn("and", ae.getOp1(), ae.getOp2()));
	}
	
	public void caseOrExpr(OrExpr oe) {
		stmtV.addInsn(buildCalculatingBinaryInsn("or", oe.getOp1(), oe.getOp2()));
	}
	
	public void caseXorExpr(XorExpr xe) {
		stmtV.addInsn(buildCalculatingBinaryInsn("xor", xe.getOp1(), xe.getOp2()));
	}
	
	public void caseShlExpr(ShlExpr sle) {
		stmtV.addInsn(buildCalculatingBinaryInsn("shl", sle.getOp1(), sle.getOp2()));
	}
	
	public void caseShrExpr(ShrExpr sre) {
		stmtV.addInsn(buildCalculatingBinaryInsn("shr", sre.getOp1(), sre.getOp2()));
	}

	public void caseUshrExpr(UshrExpr usre) {
		stmtV.addInsn(buildCalculatingBinaryInsn("ushr", usre.getOp1(), usre.getOp2()));
	}
	
	private Insn buildComparingBinaryInsn(String binaryOperation, Value firstOperand, Value secondOperand) {
		Value realFirstOperand = fixNullConstant(firstOperand);
		Value realSecondOperand = fixNullConstant(secondOperand);
		Register firstOpReg = regAlloc.asImmediate(realFirstOperand, constantV);
		// select fitting opcode ("if" or "if-zero")
		InsnWithOffset comparingBinaryInsn;
		String opcodeName = "if-" + binaryOperation;
		boolean secondOpIsInt = realSecondOperand instanceof IntConstant;
		boolean secondOpIsZero = secondOpIsInt && ((IntConstant) realSecondOperand).value == 0;
		if (secondOpIsZero) {
			Opcode opc = Opcode.getOpcodeByName(opcodeName.concat("z"));
			comparingBinaryInsn = new Insn21t(opc, firstOpReg);
			comparingBinaryInsn.setOffset(targetForOffset);<commit_msg>found opportunity to implement not-int and not-long opcodes (useful if jimple code was generated by dexpler)<commit_after>package soot.toDex;

import java.util.ArrayList;
import java.util.List;

import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.Code.Opcode;

import soot.ArrayType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.IntegerType;
import soot.LongType;
import soot.SootClass;
import soot.Type;
import soot.Value;
import soot.jimple.AddExpr;
import soot.jimple.AndExpr;
import soot.jimple.CastExpr;
import soot.jimple.CmpExpr;
import soot.jimple.CmpgExpr;
import soot.jimple.CmplExpr;
import soot.jimple.DivExpr;
import soot.jimple.DynamicInvokeExpr;
import soot.jimple.EqExpr;
import soot.jimple.ExprSwitch;
import soot.jimple.GeExpr;
import soot.jimple.GtExpr;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InstanceOfExpr;
import soot.jimple.IntConstant;
import soot.jimple.InterfaceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.LeExpr;
import soot.jimple.LengthExpr;
import soot.jimple.LongConstant;
import soot.jimple.LtExpr;
import soot.jimple.MulExpr;
import soot.jimple.NeExpr;
import soot.jimple.NegExpr;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.NullConstant;
import soot.jimple.OrExpr;
import soot.jimple.RemExpr;
import soot.jimple.ShlExpr;
import soot.jimple.ShrExpr;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.SubExpr;
import soot.jimple.UshrExpr;
import soot.jimple.VirtualInvokeExpr;
import soot.jimple.XorExpr;
import soot.toDex.instructions.Insn;
import soot.toDex.instructions.Insn11x;
import soot.toDex.instructions.Insn12x;
import soot.toDex.instructions.Insn21c;
import soot.toDex.instructions.Insn21t;
import soot.toDex.instructions.Insn22b;
import soot.toDex.instructions.Insn22c;
import soot.toDex.instructions.Insn22s;
import soot.toDex.instructions.Insn22t;
import soot.toDex.instructions.Insn23x;
import soot.toDex.instructions.Insn35c;
import soot.toDex.instructions.Insn3rc;
import soot.toDex.instructions.InsnWithOffset;
import soot.util.Switchable;

/**
 * A visitor that builds a list of instructions from the Jimple expressions it visits.<br>
 * <br>
 * Use {@link Switchable#apply(soot.util.Switch)} with this visitor to add statements.
 * These are added to the instructions in the {@link StmtVisitor}.<br>
 * If the expression is part of an assignment or an if statement, use {@link #setDestinationReg(Register)}
 * and {@link #setTargetForOffset(Stmt)}, respectively.
 * 
 * @see StmtVisitor
 */
public class ExprVisitor implements ExprSwitch {
	
	private StmtVisitor stmtV;
	
	private ConstantVisitor constantV;
	
	private RegisterAllocator regAlloc;
	
	private Register destinationReg;
	
	private Stmt targetForOffset;
	
	public ExprVisitor(StmtVisitor stmtV, ConstantVisitor constantV, RegisterAllocator regAlloc) {
		this.stmtV = stmtV;
		this.constantV = constantV;
		this.regAlloc = regAlloc;
	}
	
	public void setDestinationReg(Register destinationReg) {
		this.destinationReg = destinationReg;
	}
	
	public void setTargetForOffset(Stmt targetForOffset) {
		this.targetForOffset = targetForOffset;
	}

	public void defaultCase(Object o) {
		// rsub-int and rsub-int/lit8 aren't generated, since we cannot detect there usage in jimple
		throw new Error("unknown Object (" + o.getClass() + ") as Expression: " + o);
	}

	public void caseDynamicInvokeExpr(DynamicInvokeExpr v) {
		throw new Error("DynamicInvokeExpr not supported: " + v);
	}
	
	public void caseSpecialInvokeExpr(SpecialInvokeExpr sie) {
		MethodIdItem method = DexPrinter.toMethodIdItem(sie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> arguments = getInstanceInvokeArgumentRegs(sie);
		if (isCallToConstructor(sie) || isCallToPrivate(sie)) {
			stmtV.addInsn(buildInvokeInsn("invoke-direct", method, arguments));
		} else if (isCallToSuper(sie)) {
			stmtV.addInsn(buildInvokeInsn("invoke-super", method, arguments));
		} else
			throw new Error("unknown SpecialInvokeExpr (no call to constructor, super or private method): " + sie);
	}

	private Insn buildInvokeInsn(String invokeOpcode, MethodIdItem method, List<Register> argumentRegs) {
		Insn invokeInsn;
		int regCountForArguments = SootToDexUtils.getRealRegCount(argumentRegs);
		if (regCountForArguments <= 5) {
			Register[] paddedArray = pad35cRegs(argumentRegs);
			Opcode opc = Opcode.getOpcodeByName(invokeOpcode);
			invokeInsn = new Insn35c(opc, regCountForArguments, paddedArray[0], paddedArray[1], paddedArray[2], paddedArray[3], paddedArray[4], method);
		} else if (regCountForArguments <= 255) {
			Opcode opc = Opcode.getOpcodeByName(invokeOpcode + "/range");
			invokeInsn = new Insn3rc(opc, argumentRegs, (short) regCountForArguments, method);
		} else {
			throw new Error("too many parameter registers for invoke-* (> 255): " + regCountForArguments + " or registers too big (> 4 bits)");
		}
		// save the return type for the move-result insn
		stmtV.setLastReturnTypeDescriptor(method.getPrototype().getReturnType().getTypeDescriptor());
		return invokeInsn;
	}

	private boolean isCallToPrivate(SpecialInvokeExpr sie) {
		return sie.getMethod().isPrivate();
	}

	private boolean isCallToConstructor(SpecialInvokeExpr sie) {
		return sie.getMethod().isConstructor();
	}

	private boolean isCallToSuper(SpecialInvokeExpr sie) {
		SootClass classWithInvokation = sie.getMethod().getDeclaringClass();
		SootClass currentClass = stmtV.getBelongingClass();
		while (currentClass.hasSuperclass()) {
			currentClass = currentClass.getSuperclass();
			if (currentClass.equals(classWithInvokation)) {
				return true;
			}
		}
		return false; // we arrived at java.lang.Object and did not find a declaration
	}

	public void caseVirtualInvokeExpr(VirtualInvokeExpr vie) {
		/*
		 * for final methods we build an invoke-virtual opcode, too, although the dex spec says that a virtual method is not final.
		 * An alternative would be the invoke-direct opcode, but this is inconsistent with dx's output...
		 */
		MethodIdItem method = DexPrinter.toMethodIdItem(vie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> argumentRegs = getInstanceInvokeArgumentRegs(vie);
		stmtV.addInsn(buildInvokeInsn("invoke-virtual", method, argumentRegs));
	}
	
	private List<Register> getInvokeArgumentRegs(InvokeExpr ie) {
		List<Register> argumentRegs = new ArrayList<Register>();
		for (Value arg : ie.getArgs()) {
			Register currentReg = regAlloc.asImmediate(arg, constantV);
			argumentRegs.add(currentReg);
		}
		return argumentRegs;
	}

	private List<Register> getInstanceInvokeArgumentRegs(InstanceInvokeExpr iie) {
		List<Register> argumentRegs = getInvokeArgumentRegs(iie);
		// always add reference to callee as first parameter (instance != static)
		Value callee = iie.getBase();
		Register calleeRegister = regAlloc.asLocal(callee);
		argumentRegs.add(0, calleeRegister);
		return argumentRegs;
	}

	private Register[] pad35cRegs(List<Register> realRegs) {
		// pad real registers to an array of five bytes, as required by the instruction format "35c"
		Register[] paddedArray = new Register[5];
		int nextReg = 0;
		for (Register realReg : realRegs) {
			paddedArray[nextReg] = realReg;
			/*
			 * we include the second half of a wide with an empty reg for the "gap".
			 * this will be made explicit for dexlib later on.
			 */
			if (realReg.isWide()) {
				nextReg++;
				paddedArray[nextReg] = Register.EMPTY_REGISTER;
			}
			nextReg++;
		}
		// do the real padding with empty regs
		for (; nextReg < 5; nextReg++) {
			paddedArray[nextReg] = Register.EMPTY_REGISTER;
		}
		return paddedArray;
	}

	public void caseInterfaceInvokeExpr(InterfaceInvokeExpr iie) {
		MethodIdItem method = DexPrinter.toMethodIdItem(iie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> arguments = getInstanceInvokeArgumentRegs(iie);
		stmtV.addInsn(buildInvokeInsn("invoke-interface", method, arguments));
	}

	public void caseStaticInvokeExpr(StaticInvokeExpr sie) {
		MethodIdItem method = DexPrinter.toMethodIdItem(sie.getMethodRef(), stmtV.getBelongingFile());
		List<Register> arguments = getInvokeArgumentRegs(sie);
		stmtV.addInsn(buildInvokeInsn("invoke-static", method, arguments));
	}
	
	private Insn buildCalculatingBinaryInsn(String binaryOperation, Value firstOperand, Value secondOperand) {
		/*
		 * it is assumed (but not enforced!) that the types of firstOperand, secondOperand and destinationRegister are compatible
		 */
		Register firstOpReg = regAlloc.asImmediate(firstOperand, constantV);
		
		// use special "/lit"-opcodes if int is the destination's type, the second op is an integer literal and the opc is not sub (no sub*/lit opc available)
		if (destinationReg.getType() instanceof IntType && secondOperand instanceof IntConstant && !binaryOperation.equals("sub")) {
			int secondOpConstant = ((IntConstant) secondOperand).value;
			if (SootToDexUtils.fitsSigned8(secondOpConstant)) {
				return buildLit8BinaryInsn(binaryOperation, firstOpReg, (byte) secondOpConstant);
			}
			if (SootToDexUtils.fitsSigned16(secondOpConstant)) {
				if (!binaryOperation.equals("shl") && !binaryOperation.equals("shr") && !binaryOperation.equals("ushr")) {
					// no shift opc available for /lit16
					return buildLit16BinaryInsn(binaryOperation, firstOpReg, (short) secondOpConstant);
				}
			}
			// constant is too big, so use it as second op and build normally
		}
		Register secondOpReg = regAlloc.asImmediate(secondOperand, constantV);
		// use special "/2addr"-opcodes if destination equals first op
		if (destinationReg.getNumber() == firstOpReg.getNumber()) {
			return build2AddrBinaryInsn(binaryOperation, secondOpReg);
		}
		// no optimized binary opcode possible
		return buildNormalBinaryInsn(binaryOperation, firstOpReg, secondOpReg);
	}

	private String fixIntTypeString(String typeString) {
		if (typeString.equals("boolean") || typeString.equals("byte") || typeString.equals("char") || typeString.equals("short")) {
			return "int";
		}
		return typeString;
	}

	private Insn build2AddrBinaryInsn(String binaryOperation, Register secondOpReg) {
		String localTypeString = destinationReg.getTypeString();
		localTypeString = fixIntTypeString(localTypeString);
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-" + localTypeString + "/2addr");
		return new Insn12x(opc, destinationReg, secondOpReg);
	}

	private Insn buildNormalBinaryInsn(String binaryOperation, Register firstOpReg, Register secondOpReg) {
		String localTypeString = firstOpReg.getTypeString();
		localTypeString = fixIntTypeString(localTypeString);
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-" + localTypeString);
		return new Insn23x(opc, destinationReg, firstOpReg, secondOpReg);
	}
	
	private Insn buildLit16BinaryInsn(String binaryOperation, Register firstOpReg, short secondOpLieteral) {
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-int/lit16");
		return new Insn22s(opc, destinationReg, firstOpReg, secondOpLieteral);
	}

	private Insn buildLit8BinaryInsn(String binaryOperation, Register firstOpReg, byte secondOpLiteral) {
		Opcode opc = Opcode.getOpcodeByName(binaryOperation + "-int/lit8");
		return new Insn22b(opc, destinationReg, firstOpReg, secondOpLiteral);
	}

	public void caseAddExpr(AddExpr ae) {
		stmtV.addInsn(buildCalculatingBinaryInsn("add", ae.getOp1(), ae.getOp2()));
	}
	
	public void caseSubExpr(SubExpr se) {
		stmtV.addInsn(buildCalculatingBinaryInsn("sub", se.getOp1(), se.getOp2()));
	}
	
	public void caseMulExpr(MulExpr me) {
		stmtV.addInsn(buildCalculatingBinaryInsn("mul", me.getOp1(), me.getOp2()));
	}
	
	public void caseDivExpr(DivExpr de) {
		stmtV.addInsn(buildCalculatingBinaryInsn("div", de.getOp1(), de.getOp2()));
	}
	
	public void caseRemExpr(RemExpr re) {
		stmtV.addInsn(buildCalculatingBinaryInsn("rem", re.getOp1(), re.getOp2()));
	}
	
	public void caseAndExpr(AndExpr ae) {
		stmtV.addInsn(buildCalculatingBinaryInsn("and", ae.getOp1(), ae.getOp2()));
	}
	
	public void caseOrExpr(OrExpr oe) {
		stmtV.addInsn(buildCalculatingBinaryInsn("or", oe.getOp1(), oe.getOp2()));
	}
	
	public void caseXorExpr(XorExpr xe) {
		Value firstOperand = xe.getOp1();
		Value secondOperand = xe.getOp2();
		// see for unary ones-complement shortcut, may be a result of Dexpler's conversion
		if (secondOperand.equals(IntConstant.v(-1))) {
			Register sourceReg = regAlloc.asImmediate(firstOperand, constantV);
			stmtV.addInsn(new Insn12x(Opcode.NOT_INT, destinationReg, sourceReg));
		} else if (secondOperand.equals(LongConstant.v(-1))) {
			Register sourceReg = regAlloc.asImmediate(firstOperand, constantV);
			stmtV.addInsn(new Insn12x(Opcode.NOT_LONG, destinationReg, sourceReg));
		} else {
			stmtV.addInsn(buildCalculatingBinaryInsn("xor", firstOperand, secondOperand));
		}
	}
	
	public void caseShlExpr(ShlExpr sle) {
		stmtV.addInsn(buildCalculatingBinaryInsn("shl", sle.getOp1(), sle.getOp2()));
	}
	
	public void caseShrExpr(ShrExpr sre) {
		stmtV.addInsn(buildCalculatingBinaryInsn("shr", sre.getOp1(), sre.getOp2()));
	}

	public void caseUshrExpr(UshrExpr usre) {
		stmtV.addInsn(buildCalculatingBinaryInsn("ushr", usre.getOp1(), usre.getOp2()));
	}
	
	private Insn buildComparingBinaryInsn(String binaryOperation, Value firstOperand, Value secondOperand) {
		Value realFirstOperand = fixNullConstant(firstOperand);
		Value realSecondOperand = fixNullConstant(secondOperand);
		Register firstOpReg = regAlloc.asImmediate(realFirstOperand, constantV);
		// select fitting opcode ("if" or "if-zero")
		InsnWithOffset comparingBinaryInsn;
		String opcodeName = "if-" + binaryOperation;
		boolean secondOpIsInt = realSecondOperand instanceof IntConstant;
		boolean secondOpIsZero = secondOpIsInt && ((IntConstant) realSecondOperand).value == 0;
		if (secondOpIsZero) {
			Opcode opc = Opcode.getOpcodeByName(opcodeName.concat("z"));
			comparingBinaryInsn = new Insn21t(opc, firstOpReg);
			comparingBinaryInsn.setOffset(targetForOffset);
{'fraction_non_alphanumeric': 0.10634423483137884, 'fraction_numerical': 0.00742952873479496, 'mean_word_length': 5.539414146225292, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 35, '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': '2246539', 'n_tokens_mistral': 9515, 'n_tokens_neox': 8677, 'n_words': 2008}