/******************************************************************************* * Copyright (c) 2002 - 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Adam Fuchs, Avik Chaudhuri, Steve Suh - Modified from stack to registers *******************************************************************************/ package com.ibm.wala.dalvik.ssa; import java.nio.ByteBuffer; import java.util.Iterator; import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction.ArrayElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ibm.wala.cfg.IBasicBlock; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.Language; import com.ibm.wala.dalvik.classLoader.DexCFG; import com.ibm.wala.dalvik.classLoader.DexCFG.BasicBlock; import com.ibm.wala.dalvik.classLoader.DexIMethod; import com.ibm.wala.dalvik.classLoader.Literal; import com.ibm.wala.dalvik.dex.instructions.ArrayFill; import com.ibm.wala.dalvik.dex.instructions.ArrayGet; import com.ibm.wala.dalvik.dex.instructions.ArrayLength; import com.ibm.wala.dalvik.dex.instructions.ArrayPut; import com.ibm.wala.dalvik.dex.instructions.BinaryLiteralOperation; import com.ibm.wala.dalvik.dex.instructions.BinaryOperation; import com.ibm.wala.dalvik.dex.instructions.Branch; import com.ibm.wala.dalvik.dex.instructions.CheckCast; import com.ibm.wala.dalvik.dex.instructions.Constant; import com.ibm.wala.dalvik.dex.instructions.GetField; import com.ibm.wala.dalvik.dex.instructions.Goto; import com.ibm.wala.dalvik.dex.instructions.InstanceOf; import com.ibm.wala.dalvik.dex.instructions.Instruction; import com.ibm.wala.dalvik.dex.instructions.Instruction.Visitor; import com.ibm.wala.dalvik.dex.instructions.Invoke; import com.ibm.wala.dalvik.dex.instructions.Monitor; import com.ibm.wala.dalvik.dex.instructions.New; import com.ibm.wala.dalvik.dex.instructions.NewArray; import com.ibm.wala.dalvik.dex.instructions.NewArrayFilled; import com.ibm.wala.dalvik.dex.instructions.PutField; import com.ibm.wala.dalvik.dex.instructions.Return; import com.ibm.wala.dalvik.dex.instructions.Switch; import com.ibm.wala.dalvik.dex.instructions.Throw; import com.ibm.wala.dalvik.dex.instructions.UnaryOperation; import com.ibm.wala.shrikeBT.ArrayLengthInstruction; import com.ibm.wala.shrikeBT.CheckCastInstruction; import com.ibm.wala.shrikeBT.ConstantInstruction; import com.ibm.wala.shrikeBT.GotoInstruction; import com.ibm.wala.shrikeBT.IArrayLoadInstruction; import com.ibm.wala.shrikeBT.IArrayStoreInstruction; import com.ibm.wala.shrikeBT.IBinaryOpInstruction; import com.ibm.wala.shrikeBT.IConditionalBranchInstruction; import com.ibm.wala.shrikeBT.IGetInstruction; import com.ibm.wala.shrikeBT.IInvokeInstruction; import com.ibm.wala.shrikeBT.IUnaryOpInstruction; import com.ibm.wala.shrikeBT.InstanceofInstruction; import com.ibm.wala.shrikeBT.MonitorInstruction; import com.ibm.wala.shrikeBT.NewInstruction; import com.ibm.wala.shrikeBT.ReturnInstruction; import com.ibm.wala.shrikeBT.SwitchInstruction; import com.ibm.wala.shrikeBT.ThrowInstruction; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.ISSABasicBlock; import com.ibm.wala.ssa.PhiValue; import com.ibm.wala.ssa.SSAAbstractInvokeInstruction; import com.ibm.wala.ssa.SSACFG; import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock; import com.ibm.wala.ssa.SSAConditionalBranchInstruction; import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInstructionFactory; import com.ibm.wala.ssa.SSAInvokeInstruction; import com.ibm.wala.ssa.SSALoadMetadataInstruction; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.ssa.SSAPiInstruction; import com.ibm.wala.ssa.SSAPiNodePolicy; import com.ibm.wala.ssa.ShrikeIndirectionData; import com.ibm.wala.ssa.SymbolTable; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.FieldReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.Pair; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.graph.dominators.Dominators; import com.ibm.wala.util.intset.IntPair; /** * This class constructs an SSA {@link IR} from a backing ShrikeBT instruction stream. * * The basic algorithm here is an abstract interpretation over the Java bytecode to determine types of stack locations and local * variables. As a side effect, the flow functions of the abstract interpretation emit instructions, eliminating the stack * abstraction and moving to a register-transfer language in SSA form. */ public class DexSSABuilder extends AbstractIntRegisterMachine { private static final Logger logger = LoggerFactory.getLogger(DexSSABuilder.class); public static DexSSABuilder make(DexIMethod method, SSACFG cfg, DexCFG scfg, SSAInstruction[] instructions, SymbolTable symbolTable, boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) throws IllegalArgumentException { if (scfg == null) { throw new IllegalArgumentException("scfg == null"); } return new DexSSABuilder(method, cfg, scfg, instructions, symbolTable, buildLocalMap, piNodePolicy); } /** * A wrapper around the method being analyzed. */ final private DexIMethod method; /** * Governing symbol table */ final private SymbolTable symbolTable; /** * A logical mapping from <bcIndex, valueNumber> -> local number if null, don't build it. */ private final SSA2LocalMap localMap; /** * a factory to create concrete instructions */ private final SSAInstructionFactory insts; /** * information about indirect use of local variables in the bytecode */ // private final IndirectionData bytecodeIndirections; private final ShrikeIndirectionData shrikeIndirections; private DexSSABuilder(DexIMethod method, SSACFG cfg, DexCFG scfg, SSAInstruction[] instructions, SymbolTable symbolTable, boolean buildLocalMap, SSAPiNodePolicy piNodePolicy) { super(scfg); localMap = buildLocalMap ? new SSA2LocalMap(scfg, instructions.length, cfg.getNumberOfNodes(), method.getMaxLocals()) : null; init(new SymbolTableMeeter(cfg, instructions, scfg), new SymbolicPropagator(scfg, instructions, localMap, cfg, piNodePolicy)); this.method = method; this.symbolTable = symbolTable; this.insts = method.getDeclaringClass().getClassLoader().getInstructionFactory(); // this.bytecodeIndirections = method.getIndirectionData(); this.shrikeIndirections = new ShrikeIndirectionData(instructions.length); assert cfg != null : "Null CFG"; } private class SymbolTableMeeter implements Meeter { final SSACFG cfg; final DexCFG dexCFG; SymbolTableMeeter(SSACFG cfg, SSAInstruction[] instructions, DexCFG dexCFG) { this.cfg = cfg; // this.instructions = instructions; this.dexCFG = dexCFG; } // public int meetStack(int slot, int[] rhs, IBasicBlock<Instruction> bb) { // // assert bb != null : "null basic block"; // // if (bb.isExitBlock()) { // return TOP; // } // // if (allTheSame(rhs)) { // for (int i = 0; i < rhs.length; i++) { // if (rhs[i] != TOP) { // return rhs[i]; // } // } // // didn't find anything but TOP // return TOP; // } else { // SSACFG.BasicBlock newBB = cfg.getNode(dexCFG.getNumber(bb)); // // if we already have a phi for this stack location // SSAPhiInstruction phi = newBB.getPhiForStackSlot(slot); // int result; // if (phi == null) { // // no phi already exists. create one. // result = symbolTable.newPhi(rhs); // PhiValue v = symbolTable.getPhiValue(result); // phi = v.getPhiInstruction(); // newBB.addPhiForStackSlot(slot, phi); // } else { // // already created a phi. update it to account for the // // new merge. // result = phi.getDef(); // phi.setValues(rhs.clone()); // } // return result; // } // } public int meetLocal(int n, int[] rhs, DexCFG.BasicBlock bb) { if (allTheSame(rhs)) { for (int i = 0; i < rhs.length; i++) { if (rhs[i] != TOP) { return rhs[i]; } } // didn't find anything but TOP return TOP; } else { SSACFG.BasicBlock newBB = cfg.getNode(dexCFG.getNumber(bb)); if (bb.isExitBlock()) { // no phis in exit block please return TOP; } // if we already have a phi for this local SSAPhiInstruction phi = newBB.getPhiForLocal(n); int result; if (phi == null) { // no phi already exists. create one. result = symbolTable.newPhi(rhs); PhiValue v = symbolTable.getPhiValue(result); phi = v.getPhiInstruction(); newBB.addPhiForLocal(n, phi); } else { // already created a phi. update it to account for the // new merge. result = phi.getDef(); phi.setValues(rhs.clone()); } return result; } } /** * Are all rhs values all the same? Note, we consider TOP (-1) to be same as everything else. * * @param rhs * @return boolean */ private boolean allTheSame(int[] rhs) { int x = -1; // set x := the first non-TOP value int i = 0; for (i = 0; i < rhs.length; i++) { if (rhs[i] != TOP) { x = rhs[i]; break; } } // check the remaining values for (i = i + 1; i < rhs.length; i++) { if (rhs[i] != x && rhs[i] != TOP) return false; } return true; } } @Override protected void initializeVariables() { MachineState entryState = getEntryState(); int parameterNumber = 0; // added -2 to account for the return and exception register // int local = method.getMaxLocals() - method.getNumberOfParameters() - 1 - 2; //can't use just getNumberOfParameters because it does not account for Long/Wide variables which take up 2 registers //as the parameter int local = method.getMaxLocals() - method.getNumberOfParameterRegisters() - 1 - 2; // MyLogger.log(LogLevel.DEBUG, "DexSSABuilder - initializeVariables() - local: " + local); //initialize the "this" parameter if it needs to be set. //the "this" parameter will be symbol number 1 in a virtual method. // if (method.getMaxLocals() - method.getNumberOfParameters() - 2 > 0) // entryState.setLocal(local, 1); // System.out.println("visiting initalizeVartiables()"); // if (method.isStatic()) // System.out.println("Static"); // if (method.isClinit()) // System.out.println("Clinit"); // System.out.println("GetNumberOfParameter: " + method.getNumberOfParameterRegisters()); // System.out.println("Total Registers: " + (method.getMaxLocals()-2)); // System.out.println("local: " + local); // if (local >= 0) { // System.out.println("Max Registers: " + (int)(method.getMaxLocals() - 2)); // System.out.println("Parameters: " + method.getNumberOfParameters()); // System.out.println("Setting Entry State, local:"+ local + " with 1"); // entryState.setLocal(local, 1); // } // System.out.println("Max Registers: " + (int)(method.getMaxLocals() - 2)); // System.out.println("Parameters: " + method.getNumberOfParameters()); entryState.allocateLocals(); for (int i = 0; i < method.getNumberOfParameters(); i++) { local++; TypeReference t = method.getParameterType(i); if (t != null) { int symbol = symbolTable.getParameter(parameterNumber++); entryState.setLocal(local, symbol); if (t.equals(TypeReference.Double) || t.equals(TypeReference.Long)) { local++; entryState.setLocal(local,symbol); } } } // MyLogger.log(LogLevel.DEBUG, "DexSSABuilder - initializeVariables() - local: " + local); // This useless value ensures that the state cannot be empty, even // for a static method with no arguments in blocks with an empty stack // and no locals being used. This ensures that propagation of the // state thru the CFGSystem will always show changes the first time // it reaches a block, and thus no piece of the CFG will be skipped. // // (note that this bizarre state really happened, in java_cup) // // SJF: I don't understand how this is supposed to work. It // causes a bug right now in normal cases, so I'm commenting it out // for now. If there's a problem, let's add a regression test // to catch it. // // TODO: if there's no stack do we need this? //entryState.push(symbolTable.newSymbol()); } /** * This class defines the type abstractions for this analysis and the flow function for each instruction in the ShrikeBT IR. */ private class SymbolicPropagator extends BasicRegisterFlowProvider { final SSAInstruction[] instructions; final DexCFG dexCFG; final SSACFG cfg; final ClassLoaderReference loader; /** * creators[i] holds the instruction that defs value number i */ private SSAInstruction[] creators; // final SSA2LocalMap localMap; final SSAPiNodePolicy piNodePolicy; public SymbolicPropagator(DexCFG dexCFG, SSAInstruction[] instructions, SSA2LocalMap localMap, SSACFG cfg, SSAPiNodePolicy piNodePolicy) { super(dexCFG); this.piNodePolicy = null;// piNodePolicy; this.cfg = cfg; this.creators = new SSAInstruction[0]; this.dexCFG = dexCFG; this.instructions = instructions; this.loader = dexCFG.getMethod().getDeclaringClass().getClassLoader().getReference(); // this.localMap = localMap; init(this.new NodeVisitor(cfg), this.new EdgeVisitor()); } @Override public boolean needsEdgeFlow() { return piNodePolicy != null; } private void emitInstruction(SSAInstruction s) { logger.debug("Setting instruction "+getCurrentInstructionIndex()+" to "+s); instructions[getCurrentInstructionIndex()] = s; for (int i = 0; i < s.getNumberOfDefs(); i++) { if (creators.length < (s.getDef(i) + 1)) { SSAInstruction[] arr = new SSAInstruction[2 * s.getDef(i)]; System.arraycopy(creators, 0, arr, 0, creators.length); creators = arr; } assert s.getDef(i) != -1 : "invalid def " + i + " for " + s; creators[s.getDef(i)] = s; } } private SSAInstruction getCurrentInstruction() { return instructions[getCurrentInstructionIndex()]; } /** * If we've already created the current instruction, return the value number def'ed by the current instruction. Else, create a * new symbol. */ /** BEGIN custom change: Bugfix for the case that the instruction does not have a def */ private int reuseOrCreateDef() { if (getCurrentInstruction() == null || !getCurrentInstruction().hasDef()) { return symbolTable.newSymbol(); } else { return getCurrentInstruction().getDef(); } } /** END custom change: Bugfix for the case that the instruction does not have a def */ /** * If we've already created the current instruction, return the value number representing the exception the instruction may * throw. Else, create a new symbol */ private int reuseOrCreateException() { if (getCurrentInstruction() != null) { assert getCurrentInstruction() instanceof SSAInvokeInstruction; } if (getCurrentInstruction() == null) { return symbolTable.newSymbol(); } else { SSAInvokeInstruction s = (SSAInvokeInstruction) getCurrentInstruction(); return s.getException(); } } /** * Update the machine state to account for an instruction */ class NodeVisitor extends BasicRegisterMachineVisitor { private final SSACFG cfg; public NodeVisitor(SSACFG cfg) { this.cfg = cfg; } // TODO: make sure all visit functions are overridden /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLength(ArrayLengthInstruction) */ @Override public void visitArrayLength(ArrayLength instruction) { int arrayRef = workingState.getLocal(instruction.source); int dest = instruction.destination; int length = reuseOrCreateDef(); setLocal(dest, length); emitInstruction(insts.ArrayLengthInstruction(getCurrentInstructionIndex(), length, arrayRef)); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(IArrayLoadInstruction) */ @Override public void visitArrayGet(ArrayGet instruction) { int index = workingState.getLocal(instruction.offset); int arrayRef = workingState.getLocal(instruction.array); int dest = instruction.destination; // int index = workingState.pop(); // int arrayRef = workingState.pop(); int result = reuseOrCreateDef(); setLocal(dest, result); // workingState.push(result); TypeReference t = instruction.getType(); // if (instruction.isAddressOf()) { // emitInstruction(insts.AddressOfInstruction(result, arrayRef, index, t)); // } else { emitInstruction(insts.ArrayLoadInstruction(getCurrentInstructionIndex(), result, arrayRef, index, t)); // } } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(IArrayStoreInstruction) */ @Override public void visitArrayPut(ArrayPut instruction) { int value = workingState.getLocal(instruction.source); int index = workingState.getLocal(instruction.offset); int arrayRef = workingState.getLocal(instruction.array); TypeReference t = instruction.getType(); //System.out.println(t.getName().toString()); // int value = workingState.pop(); // int index = workingState.pop(); // int arrayRef = workingState.pop(); // TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType()); emitInstruction(insts.ArrayStoreInstruction(getCurrentInstructionIndex(), arrayRef, index, value, t)); } @Override public void visitArrayFill(ArrayFill instruction) { Iterator<ArrayElement> iae = instruction.getTable().getElements(); int i = 0; while (iae.hasNext()) { ArrayElement ae = (ArrayElement)iae.next(); int ElementWidth = ae.elementWidth; int index = symbolTable.getConstant(i); int arrayRef = workingState.getLocal(instruction.array); TypeReference t = instruction.getType(); // System.out.println(t.getName().toString()); int value; //fetch the byte[] array for the element byte[] temp_byte = new byte[ElementWidth]; for (int j = 0; j < ElementWidth; j++) temp_byte[j] = ae.buffer[ae.bufferIndex+(ElementWidth-1)-j]; ByteBuffer byte_buffer = ByteBuffer.wrap(temp_byte); // // System.out.println("Index: " + ae.bufferIndex + ", Width: " + ae.elementWidth + ", Value: " + byte_buffer.getSomethingDependingonType ); //okay to call the getConstant(String) for a char? if (t.equals(TypeReference.Char)) value = symbolTable.getConstant(Character.toString(byte_buffer.getChar())); else if (t.equals(TypeReference.Byte)) value = symbolTable.getConstant((int)byte_buffer.get()); else if (t.equals(TypeReference.Short)) value = symbolTable.getConstant((int)byte_buffer.getShort()); else if (t.equals(TypeReference.Int)) value = symbolTable.getConstant(byte_buffer.getInt()); else if (t.equals(TypeReference.Long)) value = symbolTable.getConstant(byte_buffer.getLong()); else if (t.equals(TypeReference.Float)) value = symbolTable.getConstant(byte_buffer.getFloat()); else if (t.equals(TypeReference.Double)) value = symbolTable.getConstant(byte_buffer.getDouble()); else if (t.equals(TypeReference.Boolean)) value = symbolTable.getConstant((byte_buffer.get() == 1)?true:false); else { logger.error("Unhandled Primitive Type in visitArrayFill"); value = 0; } emitInstruction(insts.ArrayStoreInstruction(getCurrentInstructionIndex(), arrayRef, index, value, t)); // System.out.println("Index: " + t.bufferIndex + ", Value: " + t.buffer[t.bufferIndex]); i++; } } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitBinaryOp(IBinaryOpInstruction) */ @Override public void visitBinaryOperation(BinaryOperation instruction) { int val2 = workingState.getLocal(instruction.oper2); int val1 = workingState.getLocal(instruction.oper1); int dest = instruction.destination; // int val2 = workingState.pop(); // int val1 = workingState.pop(); int result = reuseOrCreateDef(); setLocal(dest, result); // workingState.push(result); // boolean isFloat = instruction.getType().equals(TYPE_double) || instruction.getType().equals(TYPE_float); emitInstruction(insts.BinaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), false, instruction.isUnsigned(), result, val1, val2, !instruction.isFloat())); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitBinaryOp(IBinaryOpInstruction) */ @Override public void visitBinaryLiteral(BinaryLiteralOperation instruction) { //int val2 = workingState.getLocal(instruction.oper2); Literal lit = instruction.oper2; int val2; if (lit instanceof Literal.IntLiteral) val2 = symbolTable.getConstant(((Literal.IntLiteral)lit).value); else if (lit instanceof Literal.LongLiteral) val2 = symbolTable.getConstant(((Literal.LongLiteral)lit).value); else if (lit instanceof Literal.DoubleLiteral) val2 = symbolTable.getConstant(((Literal.DoubleLiteral)lit).value); else val2 = symbolTable.getConstant(((Literal.FloatLiteral)lit).value); int val1 = workingState.getLocal(instruction.oper1); int dest = instruction.destination; // int val2 = workingState.pop(); // int val1 = workingState.pop(); int result = reuseOrCreateDef(); setLocal(dest, result); // workingState.push(result); // boolean isFloat = instruction.getType().equals(TYPE_double) || instruction.getType().equals(TYPE_float); try { if (instruction.isSub()) emitInstruction(insts.BinaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), false, instruction.isUnsigned(), result, val2, val1, !instruction.isFloat())); else emitInstruction(insts.BinaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), false, instruction.isUnsigned(), result, val1, val2, !instruction.isFloat())); } catch (AssertionError e) { System.err.println("When visiting Instuction " + instruction); throw e; } } protected void setLocal(int dest, int result) { assert result <= symbolTable.getMaxValueNumber(); workingState.setLocal(dest, result); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitCheckCast(CheckCastInstruction) */ @Override public void visitCheckCast(CheckCast instruction) { int val = workingState.getLocal(instruction.object); // int val = workingState.pop(); // dex does not use this result, but we need it for the SSA CheckCastInstruction int result = reuseOrCreateDef(); workingState.setLocal(instruction.object, result); // workingState.push(result); // TypeReference t = instruction.getType(); emitInstruction(insts.CheckCastInstruction(getCurrentInstructionIndex(), result, val, instruction.type, instruction.isPEI())); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConditionalBranch(IConditionalBranchInstruction) */ @Override public void visitBranch(Branch instruction) { if(instruction instanceof Branch.BinaryBranch) { Branch.BinaryBranch bbranch = (Branch.BinaryBranch)instruction; int val2 = workingState.getLocal(bbranch.oper2); int val1 = workingState.getLocal(bbranch.oper1); // int val2 = workingState.pop(); // int val1 = workingState.pop(); TypeReference t = TypeReference.Int; emitInstruction(insts.ConditionalBranchInstruction(getCurrentInstructionIndex(), instruction.getOperator(), t, val1, val2, -1)); } else if(instruction instanceof Branch.UnaryBranch) { Branch.UnaryBranch ubranch = (Branch.UnaryBranch)instruction; int val2 = symbolTable.getConstant(0); int val1 = workingState.getLocal(ubranch.oper1); TypeReference t = TypeReference.Int; emitInstruction(insts.ConditionalBranchInstruction(getCurrentInstructionIndex(), instruction.getOperator(), t, val1, val2, -1)); } else { throw new IllegalArgumentException("instruction is of an unknown subtype of Branch"); } } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConstant(ConstantInstruction) */ @Override public void visitConstant(Constant instruction) { int dest = instruction.destination; int symbol = 0; if(instruction instanceof Constant.ClassConstant) { Constant.ClassConstant constInst = (Constant.ClassConstant)instruction; // TODO: change to a symbol that represents the given IClass // symbol = symbolTable.getConstant(((Constant.ClassConstant)instruction).value); symbol = reuseOrCreateDef(); TypeReference typeRef = constInst.value; SSALoadMetadataInstruction s = insts.LoadMetadataInstruction(getCurrentInstructionIndex(), symbol, TypeReference.JavaLangClass, typeRef); emitInstruction(s); } else if(instruction instanceof Constant.IntConstant) { symbol = symbolTable.getConstant(((Constant.IntConstant)instruction).value); } else if(instruction instanceof Constant.LongConstant) { symbol = symbolTable.getConstant(((Constant.LongConstant)instruction).value); } else if(instruction instanceof Constant.StringConstant) { symbol = symbolTable.getConstant(((Constant.StringConstant)instruction).value); } else { Assertions.UNREACHABLE("unexpected constant instruction " + instruction); } setLocal(dest, symbol); // Language l = cfg.getMethod().getDeclaringClass().getClassLoader().getLanguage(); // TypeReference type = l.getConstantType(instruction.getValue()); // int symbol = 0; // if (l.isNullType(type)) { // symbol = symbolTable.getNullConstant(); // } else if (l.isIntType(type)) { // Integer value = (Integer) instruction.getValue(); // symbol = symbolTable.getConstant(value.intValue()); // } else if (l.isLongType(type)) { // Long value = (Long) instruction.getValue(); // symbol = symbolTable.getConstant(value.longValue()); // } else if (l.isFloatType(type)) { // Float value = (Float) instruction.getValue(); // symbol = symbolTable.getConstant(value.floatValue()); // } else if (l.isDoubleType(type)) { // Double value = (Double) instruction.getValue(); // symbol = symbolTable.getConstant(value.doubleValue()); // } else if (l.isStringType(type)) { // String value = (String) instruction.getValue(); // symbol = symbolTable.getConstant(value); // } else if (l.isMetadataType(type)) { // Object rval = l.getMetadataToken(instruction.getValue()); // symbol = reuseOrCreateDef(); // emitInstruction(insts.LoadMetadataInstruction(symbol, type, rval)); // } else { // Assertions.UNREACHABLE("unexpected " + type); // } // workingState.push(symbol); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitConversion(IConversionInstruction) */ // TODO: is this just a unary operation? // @Override // public void visitConversion(IConversionInstruction instruction) { // // int val = workingState.pop(); // int result = reuseOrCreateDef(); // workingState.push(result); // // TypeReference fromType = ShrikeUtil.makeTypeReference(loader, instruction.getFromType()); // TypeReference toType = ShrikeUtil.makeTypeReference(loader, instruction.getToType()); // // emitInstruction(insts.ConversionInstruction(result, val, fromType, toType, instruction.throwsExceptionOnOverflow())); // } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction) */ @Override public void visitGetField(GetField instruction) { int dest = instruction.destination; int result = reuseOrCreateDef(); FieldReference f = FieldReference.findOrCreate(loader, instruction.clazzName, instruction.fieldName, instruction.fieldType); // TODO: what is isAddressOf()? // shouldn't matter, java doesn't allow isAddressOf() if(instruction instanceof GetField.GetInstanceField) { int instance = workingState.getLocal(((GetField.GetInstanceField)instruction).instance); emitInstruction(insts.GetInstruction(getCurrentInstructionIndex(), result, instance, f)); } else if(instruction instanceof GetField.GetStaticField) { emitInstruction(insts.GetInstruction(getCurrentInstructionIndex(), result, f)); } else { throw new IllegalArgumentException("unknown subclass of GetField: "+instruction); } // if (instruction.isAddressOf()) { // int ref = instruction.isStatic()? -1: workingState.pop(); // emitInstruction(insts.AddressOfInstruction(result, ref, f, f.getFieldType())); // } else if (instruction.isStatic()) { // emitInstruction(insts.GetInstruction(result, f)); // } else { // int ref = workingState.pop(); // emitInstruction(insts.GetInstruction(result, ref, f)); // } setLocal(dest, result); // workingState.push(result); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGoto(GotoInstruction) */ @Override public void visitGoto(Goto instruction) { emitInstruction(insts.GotoInstruction(getCurrentInstructionIndex(), instruction.destination)); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInstanceof(InstanceofInstruction) */ @Override public void visitInstanceof(InstanceOf instruction) { int ref = workingState.getLocal(instruction.source); int dest = instruction.destination; // int ref = workingState.pop(); int result = reuseOrCreateDef(); setLocal(dest, result); // workingState.push(result); // TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType()); emitInstruction(insts.InstanceofInstruction(getCurrentInstructionIndex(), result, ref, instruction.type)); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitInvoke(IInvokeInstruction) */ @Override public void visitInvoke(Invoke instruction) { // TODO: can other methods do indirect reads from a dex method? logger.debug("Visiting invoke for "+instruction); if (workingState.getLocals() != null) { logger.debug("workingState: "); for(int i = 0; i < workingState.getLocals().length; i++) { logger.debug(" "+i+":"+workingState.getLocal(i)+","); } } // doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex())); // int n = instruction.getPoppedCount(); // int n = instruction.args.length; // int[] params = new int[n]; // for (int i = n - 1; i >= 0; i--) { // params[i] = workingState.pop(); // } Language lang = dexCFG.getMethod().getDeclaringClass().getClassLoader().getLanguage(); // TODO: check that the signature needed by findOrCreate can use the descriptor logger.debug("****" + instruction.clazzName + "****" + instruction.methodName + "****" + instruction.descriptor); MethodReference m = MethodReference.findOrCreate(lang, loader, instruction.clazzName, instruction.methodName, instruction.descriptor); //((DexIClass)dexCFG.getDexMethod().getDeclaringClass()).getSuperclass().get // MethodReference m = ((DexIClass)dexCFG.getDexMethod().getDeclaringClass()).loader.lookupClass(TypeName.findOrCreate(instruction.clazzName)). // this.myClass.loader.lookupClass(TypeName.findOrCreate(cname)), // ((Instruction21c)inst).getRegisterA())); logger.debug("Created method reference "+m+" from "+instruction.clazzName+" descriptor "+m.getReturnType()); IInvokeInstruction.IDispatch code = instruction.getInvocationCode(); CallSiteReference site = CallSiteReference.make(getCurrentProgramCounter(), m, code); int exc = reuseOrCreateException(); setLocal(dexCFG.getDexMethod().getExceptionReg(), exc); int n = instruction.args.length; for (int i = 0; i < m.getNumberOfParameters(); i++) if (m.getParameterType(i) == TypeReference.Double || m.getParameterType(i) == TypeReference.Long) n--; int[] params = new int[n]; int arg_i = 0; //there is no "this" parameter when calling this invoke call if (n == m.getNumberOfParameters()) { for (int i = 0; i < n; i++) { params[i] = workingState.getLocal(instruction.args[arg_i]); logger.trace("visitInvoke param["+i+"] = "+params[i]); if (m.getParameterType(i) == TypeReference.Double || m.getParameterType(i) == TypeReference.Long) arg_i++; arg_i++; } } //there is a "this" parameter in this invoke call else if (n == m.getNumberOfParameters()+1) { params[0] = workingState.getLocal(instruction.args[0]); logger.trace("visitInvoke param[0] = "+params[0]); arg_i = 1; for (int i = 0; i < (n-1); i++) { params[i+1] = workingState.getLocal(instruction.args[arg_i]); logger.trace("visitInvoke param["+(i+1)+"] = "+params[i+1]); if (m.getParameterType(i) == TypeReference.Double || m.getParameterType(i) == TypeReference.Long) arg_i++; arg_i++; } } //this should not happen else throw new UnsupportedOperationException("visitInvoke DexSSABuilder, error"); if(m.getReturnType().equals(TypeReference.Void)) { SSAInstruction inst = insts.InvokeInstruction(getCurrentInstructionIndex(), params, exc, site); //System.out.println("Emitting(1) InvokeInstruction: "+inst); emitInstruction(inst); } else { int result = reuseOrCreateDef(); assert result != -1; // TODO: check that this return register is correct //I think it be registerCount() or registerCount()+1 // int dest = dexCFG.getDexMethod().regBank.getReturnReg().regID; int dest = dexCFG.getDexMethod().getReturnReg(); setLocal(dest, result); SSAInstruction inst = insts.InvokeInstruction(getCurrentInstructionIndex(), result, params, exc, site); //System.out.println("Emitting(2) InvokeInstruction: "+inst); emitInstruction(inst); } // if (instruction.getPushedWordSize() > 0) { // int result = reuseOrCreateDef(); // workingState.push(result); // emitInstruction(insts.InvokeInstruction(result, params, exc, site)); // } else { // emitInstruction(insts.InvokeInstruction(params, exc, site)); // } // TODO: can other methods do indirect writes to a dex method? // doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), -1); } // Dex doesn't have local load or store // @Override // public void visitLocalLoad(ILoadInstruction instruction) { // if (instruction.isAddressOf()) { // int result = reuseOrCreateDef(); // // int t = workingState.getLocal(instruction.getVarIndex()); // if (t == -1) { // doIndirectWrites(new int[]{instruction.getVarIndex()}, -1); // t = workingState.getLocal(instruction.getVarIndex()); // } // // TypeReference type = ShrikeUtil.makeTypeReference(loader, instruction.getType()); // emitInstruction(insts.AddressOfInstruction(result, t, type)); // workingState.push(result); // } else { // super.visitLocalLoad(instruction); // } // } // // /* // * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitLocalStore(com.ibm.wala.shrikeBT.StoreInstruction) // */ // @Override // public void visitLocalStore(IStoreInstruction instruction) { // if (localMap != null) { // localMap.startRange(getCurrentInstructionIndex(), instruction.getVarIndex(), workingState.peek()); // } // super.visitLocalStore(instruction); // } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitMonitor(MonitorInstruction) */ @Override public void visitMonitor(Monitor instruction) { int ref = workingState.getLocal(instruction.object); // int ref = workingState.pop(); emitInstruction(insts.MonitorInstruction(getCurrentInstructionIndex(), ref, instruction.enter)); } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitNew(NewInstruction) */ @Override public void visitNew(New instruction) { int dest = instruction.destination; int result = reuseOrCreateDef(); // TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType()); // NewSiteReference ref = NewSiteReference.make(getCurrentProgramCounter(), t); // if (t.isArrayType()) { // int[] sizes = new int[instruction.getArrayBoundsCount()]; // for (int i = 0; i < instruction.getArrayBoundsCount(); i++) { // sizes[instruction.getArrayBoundsCount() - 1 - i] = workingState.pop(); // } // emitInstruction(insts.NewInstruction(result, ref, sizes)); // } else { emitInstruction(insts.NewInstruction(getCurrentInstructionIndex(), result, instruction.newSiteRef)); // popN(instruction); // } setLocal(dest, result); // workingState.push(result); } @Override public void visitNewArray(NewArray instruction) { int dest = instruction.destination; int result = reuseOrCreateDef(); int[] sizes = new int[instruction.sizes.length]; for(int i = 0; i < instruction.sizes.length; i++) { sizes[i] = workingState.getLocal(instruction.sizes[i]); } emitInstruction(insts.NewInstruction(getCurrentInstructionIndex(), result, instruction.newSiteRef, sizes)); setLocal(dest, result); } @Override public void visitNewArrayFilled(NewArrayFilled instruction) { int dest = instruction.destination; int result = reuseOrCreateDef(); int[] sizes = new int[instruction.sizes.length]; for(int i = 0; i < instruction.sizes.length; i++) { sizes[i] = symbolTable.getConstant(instruction.sizes[i]); } emitInstruction(insts.NewInstruction(getCurrentInstructionIndex(), result, instruction.newSiteRef, sizes)); setLocal(dest, result); /* * we need to emit these instructions somehow, but for now this clobbers the emitInstruction mechanism * for (int i = 0; i < instruction.args.length; i++) { int value = workingState.getLocal(instruction.args[i]); int index = symbolTable.getConstant(i); int arrayRef = result; TypeReference t = instruction.myType; emitInstruction(insts.ArrayStoreInstruction(getCurrentInstructionIndex(), arrayRef, index, value, t)); } */ } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitGet(IGetInstruction) */ @Override public void visitPutField(PutField instruction) { int value = workingState.getLocal(instruction.source); FieldReference f = FieldReference.findOrCreate(loader, instruction.clazzName, instruction.fieldName, instruction.fieldType); // int value = workingState.pop(); if (instruction instanceof PutField.PutStaticField) { // if (instruction.isStatic()) { emitInstruction(insts.PutInstruction(getCurrentInstructionIndex(), value, f)); } else if (instruction instanceof PutField.PutInstanceField) { // } else { int ref = workingState.getLocal(((PutField.PutInstanceField)instruction).instance); // int ref = workingState.pop(); emitInstruction(insts.PutInstruction(getCurrentInstructionIndex(), ref, value, f)); } else { throw new IllegalArgumentException("Unknown subclass of PutField: "+instruction); } } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitReturn(ReturnInstruction) */ @Override public void visitReturn(Return instruction) { if(instruction instanceof Return.ReturnDouble) { // TODO: figure out how to return a double Return.ReturnDouble retD = (Return.ReturnDouble)instruction; int result = workingState.getLocal(retD.source1); // boolean isPrimitive = symbolTable.isLongConstant(result) || symbolTable.isDoubleConstant(result); boolean isPrimitive = true; emitInstruction(insts.ReturnInstruction(getCurrentInstructionIndex(), result, isPrimitive)); // throw new UnsupportedOperationException("can't yet support returning doubles"); } else if (instruction instanceof Return.ReturnSingle) { Return.ReturnSingle retS = (Return.ReturnSingle)instruction; int result = workingState.getLocal(retS.source); // TODO: figure out if this is primitive or not //boolean isPrimitive = false; boolean isPrimitive = retS.isPrimitive(); emitInstruction(insts.ReturnInstruction(getCurrentInstructionIndex(), result, isPrimitive)); } else if (instruction instanceof Return.ReturnVoid) { emitInstruction(insts.ReturnInstruction(getCurrentInstructionIndex())); } // if (instruction.getPoppedCount() == 1) { // int result = workingState.pop(); // TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType()); // emitInstruction(insts.ReturnInstruction(result, t.isPrimitiveType())); // } else { // emitInstruction(insts.ReturnInstruction()); // } } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitShift(IShiftInstruction) */ // TODO: this is just a binary operation // @Override // public void visitShift(IShiftInstruction instruction) { // int val2 = workingState.pop(); // int val1 = workingState.pop(); // int result = reuseOrCreateDef(); // workingState.push(result); // emitInstruction(insts.BinaryOpInstruction(instruction.getOperator(), false, instruction.isUnsigned(), result, val1, val2, // true)); // } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitSwitch(SwitchInstruction) */ @Override public void visitSwitch(Switch instruction) { int val = workingState.getLocal(instruction.regA); // int val = workingState.pop(); // TODO: figure out if the switch offset should refer to a pc offset or an instruction id or what emitInstruction(insts.SwitchInstruction(getCurrentInstructionIndex(), val, instruction.getDefaultLabel(), instruction.getCasesAndLabels())); } private Dominators<ISSABasicBlock> dom = null; @SuppressWarnings("unused") private int findRethrowException() { int index = getCurrentInstructionIndex(); SSACFG.BasicBlock bb = cfg.getBlockForInstruction(index); if (bb.isCatchBlock()) { SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) bb; SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction(); return s.getDef(); } else { // TODO: should we really use dominators here? maybe it would be cleaner to propagate // the notion of 'current exception to rethrow' using the abstract interpreter. if (dom == null) { dom = Dominators.make(cfg, cfg.entry()); } ISSABasicBlock x = bb; while (x != null) { if (x.isCatchBlock()) { SSACFG.ExceptionHandlerBasicBlock newBB = (SSACFG.ExceptionHandlerBasicBlock) x; SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction(); return s.getDef(); } else { x = dom.getIdom(x); } } // assert false; return -1; } } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitThrow(ThrowInstruction) */ @Override public void visitThrow(Throw instruction) { int throwable = workingState.getLocal(instruction.throwable); assert symbolTable.getMaxValueNumber() >= throwable; emitInstruction(insts.ThrowInstruction(getCurrentInstructionIndex(), throwable)); // if (instruction.isRethrow()) { // workingState.clearStack(); // emitInstruction(insts.ThrowInstruction(findRethrowException())); // } else { // int exception = workingState.pop(); // workingState.clearStack(); // workingState.push(exception); // emitInstruction(insts.ThrowInstruction(exception)); // } } /** * @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitUnaryOp(IUnaryOpInstruction) */ @Override public void visitUnaryOperation(UnaryOperation instruction) { if (instruction.op == UnaryOperation.OpID.MOVE_EXCEPTION) { int idx = getCurrentInstructionIndex(); int bbidx = dexCFG.getBlockForInstruction(idx).getNumber(); ExceptionHandlerBasicBlock newBB = (ExceptionHandlerBasicBlock) cfg.getBasicBlock(bbidx); SSAGetCaughtExceptionInstruction s = newBB.getCatchInstruction(); int exceptionValue; if (s == null) { exceptionValue = symbolTable.newSymbol(); s = insts.GetCaughtExceptionInstruction(newBB.getLastInstructionIndex(), bbidx, exceptionValue); newBB.setCatchInstruction(s); } else { exceptionValue = s.getException(); } setLocal(instruction.destination, exceptionValue); return; } //System.out.println("Instruction: " + getCurrentInstructionIndex()); int val = workingState.getLocal(instruction.source); // int val = workingState.pop(); // workingState.push(result); if(instruction.isConversion()) { TypeReference fromType, toType; boolean overflows = false; // TODO: figure out if any of these can overflow switch(instruction.op) { case DOUBLETOLONG: fromType = TypeReference.Double; toType = TypeReference.Long; break; case DOUBLETOFLOAT: fromType = TypeReference.Double; toType = TypeReference.Float; break; case INTTOBYTE: fromType = TypeReference.Int; toType = TypeReference.Byte; break; case INTTOCHAR: fromType = TypeReference.Int; toType = TypeReference.Char; break; case INTTOSHORT: fromType = TypeReference.Int; toType = TypeReference.Short; break; case DOUBLETOINT: fromType = TypeReference.Double; toType = TypeReference.Int; break; case FLOATTODOUBLE: fromType = TypeReference.Float; toType = TypeReference.Double; break; case FLOATTOLONG: fromType = TypeReference.Float; toType = TypeReference.Long; break; case FLOATTOINT: fromType = TypeReference.Float; toType = TypeReference.Int; break; case LONGTODOUBLE: fromType = TypeReference.Long; toType = TypeReference.Double; break; case LONGTOFLOAT: fromType = TypeReference.Long; toType = TypeReference.Float; break; case LONGTOINT: fromType = TypeReference.Long; toType = TypeReference.Int; break; case INTTODOUBLE: fromType = TypeReference.Int; toType = TypeReference.Double; break; case INTTOFLOAT: fromType = TypeReference.Int; toType = TypeReference.Float; break; case INTTOLONG: fromType = TypeReference.Int; toType = TypeReference.Long; break; default: throw new IllegalArgumentException("unknown conversion type "+instruction.op+" in unary instruction: "+instruction); } int dest = instruction.destination; int result = reuseOrCreateDef(); setLocal(dest, result); emitInstruction(insts.ConversionInstruction(getCurrentInstructionIndex(), result, val, fromType, toType, overflows)); } else { if (instruction.op == UnaryOperation.OpID.MOVE) { setLocal(instruction.destination, workingState.getLocal(instruction.source)); } else if (instruction.op == UnaryOperation.OpID.MOVE_WIDE) { setLocal(instruction.destination, workingState.getLocal(instruction.source)); if (instruction.source == dexCFG.getDexMethod().getReturnReg()) setLocal(instruction.destination+1, workingState.getLocal(instruction.source)); else setLocal(instruction.destination+1, workingState.getLocal(instruction.source+1)); } else { int dest = instruction.destination; int result = reuseOrCreateDef(); setLocal(dest, result); emitInstruction(insts.UnaryOpInstruction(getCurrentInstructionIndex(), instruction.getOperator(), result, val)); } } } // dex doesn't have indirect reads // private void doIndirectReads(int[] locals) { // for(int i = 0; i < locals.length; i++) { // ssaIndirections.setUse(getCurrentInstructionIndex(), new ShrikeLocalName(locals[i]), workingState.getLocal(locals[i])); // } // } // dex doesn't have load or store, even with indirection // @Override // public void visitLoadIndirect(ILoadIndirectInstruction instruction) { // int addressVal = workingState.pop(); // int result = reuseOrCreateDef(); // doIndirectReads(bytecodeIndirections.indirectlyReadLocals(getCurrentInstructionIndex())); // TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getPushedType(null)); // emitInstruction(insts.LoadIndirectInstruction(result, t, addressVal)); // workingState.push(result); // } // // private void doIndirectWrites(int[] locals, int rval) { // for(int i = 0; i < locals.length; i++) { // ShrikeLocalName name = new ShrikeLocalName(locals[i]); // int idx = getCurrentInstructionIndex(); // if (ssaIndirections.getDef(idx, name) == -1) { // ssaIndirections.setDef(idx, name, rval==-1? symbolTable.newSymbol(): rval); // } // workingState.setLocal(locals[i], ssaIndirections.getDef(idx, name)); // } // } // // @Override // public void visitStoreIndirect(IStoreIndirectInstruction instruction) { // int val = workingState.pop(); // int addressVal = workingState.pop(); // doIndirectWrites(bytecodeIndirections.indirectlyWrittenLocals(getCurrentInstructionIndex()), val); // TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType()); // emitInstruction(insts.StoreIndirectInstruction(addressVal, val, t)); // } } /** * @param piCause * @param ref */ private void reuseOrCreatePi(SSAInstruction piCause, int ref) { int n = getCurrentInstructionIndex(); SSACFG.BasicBlock bb = cfg.getBlockForInstruction(n); BasicBlock path = getCurrentSuccessor(); int outNum = dexCFG.getNumber(path); SSAPiInstruction pi = bb.getPiForRefAndPath(ref, path); if (pi == null) { pi = insts.PiInstruction(getCurrentInstructionIndex(), symbolTable.newSymbol(), ref, bb.getNumber(), outNum, piCause); bb.addPiForRefAndPath(ref, path, pi); } workingState.replaceValue(ref, pi.getDef()); } // private void maybeInsertPi(int val) { // if ((addPiForFieldSelect) && (creators.length > val) && (creators[val] instanceof SSAGetInstruction) // && !((SSAGetInstruction) creators[val]).isStatic()) { // reuseOrCreatePi(creators[val], val); // } else if ((addPiForDispatchSelect) // && (creators.length > val) // && (creators[val] instanceof SSAInvokeInstruction) // && (((SSAInvokeInstruction) creators[val]).getInvocationCode() == IInvokeInstruction.Dispatch.VIRTUAL || // ((SSAInvokeInstruction) creators[val]) // .getInvocationCode() == IInvokeInstruction.Dispatch.INTERFACE)) { // reuseOrCreatePi(creators[val], val); // } // } private void maybeInsertPi(SSAAbstractInvokeInstruction call) { if (piNodePolicy != null) { Pair<Integer, SSAInstruction> pi = piNodePolicy.getPi(call, symbolTable); if (pi != null) { reuseOrCreatePi(pi.snd, pi.fst); } } } private void maybeInsertPi(SSAConditionalBranchInstruction cond) { if (piNodePolicy != null) { Pair<Integer, SSAInstruction> pi = piNodePolicy.getPi(cond, getDef(cond.getUse(0)), getDef(cond.getUse(1)), symbolTable); if (pi != null) { reuseOrCreatePi(pi.snd, pi.fst); } } } private SSAInstruction getDef(int vn) { if (vn < creators.length) { return creators[vn]; } else { return null; } } class EdgeVisitor extends Visitor { @Override public void visitInvoke(Invoke instruction) { maybeInsertPi((SSAAbstractInvokeInstruction) getCurrentInstruction()); } @Override public void visitBranch(Branch instruction) { maybeInsertPi((SSAConditionalBranchInstruction) getCurrentInstruction()); } } @Override public Instruction[] getInstructions() { return dexCFG.getDexMethod().getDexInstructions(); } } /** * Build the IR */ public void build() { try { solve(); if (localMap != null) { localMap.finishLocalMap(this); } } catch (AssertionError e) { System.err.println("When handling method " + method.getReference()); e.printStackTrace(); //throw e; } } public SSA2LocalMap getLocalMap() { return localMap; } public ShrikeIndirectionData getIndirectionData() { return shrikeIndirections; } /** * A logical mapping from <pc, valueNumber> -> local number Note: make sure this class remains static: this persists as part of * the IR!! */ private static class SSA2LocalMap implements com.ibm.wala.ssa.IR.SSA2LocalMap { private final DexCFG dexCFG; /** * Mapping Integer -> IntPair where p maps to (vn,L) iff we've started a range at pc p where value number vn corresponds to * local L */ private final IntPair[] localStoreMap; /** * For each basic block i and local j, block2LocalState[i][j] gives the contents of local j at the start of block i */ private final int[][] block2LocalState; /** * maximum number of locals used at any program point */ private final int maxLocals; /** * @param nInstructions number of instructions in the bytecode for this method * @param nBlocks number of basic blocks in the CFG */ SSA2LocalMap(DexCFG dexCfg, int nInstructions, int nBlocks, int maxLocals) { dexCFG = dexCfg; localStoreMap = new IntPair[nInstructions]; block2LocalState = new int[nBlocks][]; this.maxLocals = maxLocals; } /** * Record the beginning of a new range, starting at the given program counter, in which a particular value number corresponds to * a particular local number */ @SuppressWarnings("unused") void startRange(int pc, int localNumber, int valueNumber) { int max = ((DexIMethod)dexCFG.getMethod()).getMaxLocals(); if (localNumber >= max) { assert false : "invalid local " + localNumber + ">" + max; } localStoreMap[pc] = new IntPair(valueNumber, localNumber); } /** * Finish populating the map of local variable information */ private void finishLocalMap(DexSSABuilder builder) { for (Iterator<BasicBlock> it = dexCFG.iterator(); it.hasNext();) { BasicBlock bb = it.next(); MachineState S = builder.getIn(bb); int number = bb.getNumber(); block2LocalState[number] = S.getLocals(); } } /** * @param index - index into IR instruction array * @param vn - value number */ public String[] getLocalNames(int index, int vn) { try { if (!dexCFG.getMethod().hasLocalVariableTable()) { return null; } else { int[] localNumbers = findLocalsForValueNumber(index, vn); if (localNumbers == null) { return null; } else { DexIMethod m = dexCFG.getDexMethod(); String[] result = new String[localNumbers.length]; for (int i = 0; i < localNumbers.length; i++) { result[i] = m.getLocalVariableName(m.getBytecodeIndex(index), localNumbers[i]); } return result; } } } catch (Exception e) { e.printStackTrace(); Assertions.UNREACHABLE(); return null; } } /** * @param pc a program counter (index into ShrikeBT instruction array) * @param vn a value number * @return if we know that immediately after the given program counter, v_vn corresponds to some set of locals, then return an * array of the local numbers. else return null. */ private int[] findLocalsForValueNumber(int pc, int vn) { IBasicBlock<Instruction> bb = dexCFG.getBlockForInstruction(pc); int firstInstruction = bb.getFirstInstructionIndex(); // walk forward from the first instruction to reconstruct the // state of the locals at this pc int[] locals = block2LocalState[bb.getNumber()]; if (locals == null) { locals = allocateNewLocalsArray(); } for (int i = firstInstruction; i <= pc; i++) { if (localStoreMap[i] != null) { IntPair p = localStoreMap[i]; locals[p.getY()] = p.getX(); } } return extractIndices(locals, vn); } public int[] allocateNewLocalsArray() { int[] result = new int[maxLocals]; for (int i = 0; i < maxLocals; i++) { result[i] = OPTIMISTIC ? TOP : BOTTOM; } return result; } /** * @return the indices i s.t. x[i] == y, or null if none found. */ private int[] extractIndices(int[] x, int y) { int count = 0; for (int i = 0; i < x.length; i++) { if (x[i] == y) { count++; } } if (count == 0) { return null; } else { int[] result = new int[count]; int j = 0; for (int i = 0; i < x.length; i++) { if (x[i] == y) { result[j++] = i; } } return result; } } } }