/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2008, Wolfgang Puffitsch * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.dfa.analyses; import com.jopdesign.common.AppInfo; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.code.CallString; import com.jopdesign.common.code.CallString.CallStringSerialization; import com.jopdesign.common.graphutils.Pair; import com.jopdesign.common.misc.AppInfoError; import com.jopdesign.common.misc.MethodNotFoundException; import com.jopdesign.common.type.MemberID; import com.jopdesign.dfa.DFATool; import com.jopdesign.dfa.framework.Analysis; import com.jopdesign.dfa.framework.AnalysisResultSerialization; import com.jopdesign.dfa.framework.AnalysisResultSerialization.ResultFormatter; import com.jopdesign.dfa.framework.AnalysisResultSerialization.Serializer; import com.jopdesign.dfa.framework.Context; import com.jopdesign.dfa.framework.ContextMap; import com.jopdesign.dfa.framework.FlowEdge; import com.jopdesign.dfa.framework.FlowEdge.SerializedFlowEdge; import com.jopdesign.dfa.framework.Interpreter; import com.jopdesign.dfa.framework.MethodHelper; import org.apache.bcel.Constants; import org.apache.bcel.classfile.LineNumberTable; import org.apache.bcel.generic.ANEWARRAY; import org.apache.bcel.generic.BranchInstruction; import org.apache.bcel.generic.ConstantPushInstruction; import org.apache.bcel.generic.GETFIELD; import org.apache.bcel.generic.GETSTATIC; import org.apache.bcel.generic.IINC; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.LDC; import org.apache.bcel.generic.LoadInstruction; import org.apache.bcel.generic.MULTIANEWARRAY; import org.apache.bcel.generic.NEWARRAY; import org.apache.bcel.generic.PUTFIELD; import org.apache.bcel.generic.PUTSTATIC; import org.apache.bcel.generic.ReferenceType; import org.apache.bcel.generic.StoreInstruction; import org.apache.bcel.generic.Type; import org.apache.log4j.Logger; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; public class LoopBounds implements Analysis<CallString, Map<Location, ValueMapping>> { private final int callStringLength; public static final String NAME = "LoopBounds"; private static final Logger logger = Logger.getLogger(DFATool.LOG_DFA_ANALYSES + "." + NAME); // used to print messages only once if trace is not enabled private Set<String> noReceiverWarnings = new LinkedHashSet<String>(); private Set<String> unknownReceiverWarnings = new LinkedHashSet<String>(); public LoopBounds(int callStringLength) { this.callStringLength = callStringLength; } public String getId() { return NAME + "-" + callStringLength; } public ContextMap<CallString, Map<Location, ValueMapping>> bottom() { return null; } public ContextMap<CallString, Map<Location, ValueMapping>> initial(InstructionHandle stmt) { ContextMap<CallString, Map<Location, ValueMapping>> retval = new ContextMap<CallString, Map<Location, ValueMapping>>(new Context(), new LinkedHashMap<CallString, Map<Location, ValueMapping>>()); CallString l = CallString.EMPTY; Map<Location, ValueMapping> init = new LinkedHashMap<Location, ValueMapping>(); ValueMapping value; value = new ValueMapping(); value.assigned.setLb(0); value.assigned.setUb(16); init.put(new Location("com.jopdesign.io.SysDevice.nrCpu"), value); retval.put(l, init); return retval; } private Map<InstructionHandle, ContextMap<CallString, Pair<ValueMapping, ValueMapping>>> bounds = new LinkedHashMap<InstructionHandle, ContextMap<CallString, Pair<ValueMapping, ValueMapping>>>(); private Map<InstructionHandle, ContextMap<CallString, Integer>> scopes = new LinkedHashMap<InstructionHandle, ContextMap<CallString, Integer>>(); private Map<InstructionHandle, ContextMap<CallString, Interval[]>> sizes = new LinkedHashMap<InstructionHandle, ContextMap<CallString, Interval[]>>(); private Map<InstructionHandle, ContextMap<CallString, Set<FlowEdge>>> infeasibles = new LinkedHashMap<InstructionHandle, ContextMap<CallString, Set<FlowEdge>>>(); private Map<InstructionHandle, ContextMap<CallString, Interval>> arrayIndices = new LinkedHashMap<InstructionHandle, ContextMap<CallString, Interval>>(); public void initialize(MethodInfo sig, Context context) { } public ContextMap<CallString, Map<Location, ValueMapping>> join( ContextMap<CallString, Map<Location, ValueMapping>> s1, ContextMap<CallString, Map<Location, ValueMapping>> s2) { if (s1 == null) { return new ContextMap<CallString, Map<Location, ValueMapping>>(s2); } if (s2 == null) { return new ContextMap<CallString, Map<Location, ValueMapping>>(s1); } ContextMap<CallString, Map<Location, ValueMapping>> result = new ContextMap<CallString, Map<Location, ValueMapping>>(new Context(s1.getContext()), new LinkedHashMap<CallString, Map<Location, ValueMapping>>()); result.putAll(s1); result.putAll(s2); Map<Location, ValueMapping> a = s1.get(s2.getContext().callString); Map<Location, ValueMapping> b = s2.get(s2.getContext().callString); if (a != null || b != null) { if (a == null) { a = new LinkedHashMap<Location, ValueMapping>(); } if (b == null) { b = new LinkedHashMap<Location, ValueMapping>(); } Map<Location, ValueMapping> merged = new LinkedHashMap<Location, ValueMapping>(a); for (Location l : b.keySet()) { ValueMapping x = a.get(l); ValueMapping y = b.get(l); if (x != null) { if (!x.equals(y)) { ValueMapping r = new ValueMapping(x, true); r.join(y); merged.put(l, r); } else { merged.put(l, x); } } else { merged.put(l, y); } } result.put(s2.getContext().callString, merged); } if (result.getContext().stackPtr < 0) { result.getContext().stackPtr = s2.getContext().stackPtr; } if (result.getContext().syncLevel < 0) { result.getContext().syncLevel = s2.getContext().syncLevel; } result.getContext().threaded = Context.isThreaded(); return result; } public boolean compare(ContextMap<CallString, Map<Location, ValueMapping>> s1, ContextMap<CallString, Map<Location, ValueMapping>> s2) { if (s1 == null || s2 == null) { return false; } if (!s1.getContext().equals(s2.getContext())) { return false; } else { Map<Location, ValueMapping> a = s1.get(s1.getContext().callString); Map<Location, ValueMapping> b = s2.get(s1.getContext().callString); if (a == null && b == null) { return true; } if (a == null || b == null) { return false; } for (Location l : a.keySet()) { if (!b.containsKey(l) || !a.get(l).compare(b.get(l))) { return false; } } return true; } } public ContextMap<CallString, Map<Location, ValueMapping>> transfer( InstructionHandle stmt, FlowEdge edge, ContextMap<CallString, Map<Location, ValueMapping>> input, Interpreter<CallString, Map<Location, ValueMapping>> interpreter, Map<InstructionHandle, ContextMap<CallString, Map<Location, ValueMapping>>> state) { Context context = new Context(input.getContext()); Map<Location, ValueMapping> in = (Map<Location, ValueMapping>) input.get(context.callString); ContextMap<CallString, Map<Location, ValueMapping>> retval = new ContextMap<CallString, Map<Location, ValueMapping>>(context, new LinkedHashMap<CallString, Map<Location, ValueMapping>>()); Instruction instruction = stmt.getInstruction(); // if (in == null) { // System.out.print(";; "); // } else if (in.isEmpty()) { // System.out.print("// "); // } // System.out.println(context.callString.asList()+"/"+context.method+": "+stmt); // if (in != null) { // System.out.println(in.get(new Location("java.io.JOPReader.lines"))); // } // shortcut for infeasible paths if (in == null) { context.stackPtr += instruction.produceStack(context.constPool()) - instruction.consumeStack(context.constPool()); return retval; } Map<Location, ValueMapping> result = new LinkedHashMap<Location, ValueMapping>(); retval.put(context.callString, result); // System.out.println(context.method+": "+stmt); // System.out.println("###"+context.stackPtr+" + "+instruction.produceStack(context.constPool)+" - "+instruction.consumeStack(context.constPool)); // System.out.println(stmt+" "+(edge.getType() == FlowEdge.TRUE_EDGE ? "TRUE" : (edge.getType() == FlowEdge.FALSE_EDGE) ? "FALSE" : "NORMAL")+" "+edge); // System.out.println(context.callString+"/"+context.method); // System.out.print(stmt.getInstruction()+":\t{ "); // System.out.print(input.get(context.callString)); // System.out.println("}"); // for (int i = 0; i < stmt.getTargeters().length; i++) { // InstructionTargeter targeter = stmt.getTargeters()[i]; // if (targeter instanceof BranchInstruction) { // checkScope(context, stmt); // break; // } // } switch (instruction.getOpcode()) { case Constants.ICONST_M1: case Constants.ICONST_0: case Constants.ICONST_1: case Constants.ICONST_2: case Constants.ICONST_3: case Constants.ICONST_4: case Constants.ICONST_5: case Constants.BIPUSH: case Constants.SIPUSH: { ConstantPushInstruction instr = (ConstantPushInstruction) instruction; result = new LinkedHashMap<Location, ValueMapping>(in); retval.put(context.callString, result); int value = instr.getValue().intValue(); result.put(new Location(context.stackPtr), new ValueMapping(value)); } break; case Constants.ACONST_NULL: result = in; retval.put(context.callString, result); break; case Constants.LDC: case Constants.LDC_W: { LDC instr = (LDC) instruction; result = new LinkedHashMap<Location, ValueMapping>(in); retval.put(context.callString, result); Type type = instr.getType(context.constPool()); if (type.equals(Type.INT)) { Integer value = (Integer) instr.getValue(context.constPool()); result.put(new Location(context.stackPtr), new ValueMapping(value)); } else if (type.equals(Type.STRING)) { String value = (String) instr.getValue(context.constPool()); String name = "char[]"; name += "@" + context.method() + ":" + stmt.getPosition(); result.put(new Location(name + ".length"), new ValueMapping(value.length())); // System.out.println(name+": \""+value+"\""); } } break; case Constants.LDC2_W: result = in; retval.put(context.callString, result); break; case Constants.ISTORE_0: case Constants.ISTORE_1: case Constants.ISTORE_2: case Constants.ISTORE_3: case Constants.ISTORE: { StoreInstruction instr = (StoreInstruction) instruction; int index = instr.getIndex(); for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1 && l.stackLoc != index) { result.put(l, in.get(l)); } if (l.stackLoc == context.stackPtr - 1) { ValueMapping v = new ValueMapping(in.get(l), true); if (in.get(l).source == null || in.get(l).source.stackLoc != index) { v.defscope = ValueMapping.scope; } result.put(new Location(index), v); } } } break; case Constants.ASTORE_0: case Constants.ASTORE_1: case Constants.ASTORE_2: case Constants.ASTORE_3: case Constants.ASTORE: result = in; retval.put(context.callString, result); break; case Constants.LSTORE_0: case Constants.LSTORE_1: case Constants.LSTORE_2: case Constants.LSTORE_3: case Constants.LSTORE: // drop top entries to avoid clobbering filterSet(in, result, context.stackPtr - 2); break; case Constants.ILOAD_0: case Constants.ILOAD_1: case Constants.ILOAD_2: case Constants.ILOAD_3: case Constants.ILOAD: { LoadInstruction instr = (LoadInstruction) instruction; filterSet(in, result, context.stackPtr); int index = instr.getIndex(); for (Location l : in.keySet()) { if (l.stackLoc == index) { ValueMapping m = new ValueMapping(in.get(l), true); m.source = l; result.put(new Location(context.stackPtr), m); } } } break; case Constants.ALOAD_0: case Constants.ALOAD_1: case Constants.ALOAD_2: case Constants.ALOAD_3: case Constants.ALOAD: result = in; retval.put(context.callString, result); break; case Constants.ARRAYLENGTH: { filterSet(in, result, context.stackPtr - 1); DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); Location location = new Location(context.stackPtr - 1); boolean valid = false; if (receivers == null) { System.out.println("no receivers at: " + context.callString.toStringList() + context.method() + stmt); } else { for (String arrayName : receivers) { ValueMapping m = in.get(new Location(arrayName + ".length")); if (m != null) { ValueMapping value = new ValueMapping(m, false); value.join(result.get(location)); result.put(location, value); valid = true; } } } if (!valid) { result.put(new Location(context.stackPtr - 1), new ValueMapping()); } } break; case Constants.PUTFIELD: { PUTFIELD instr = (PUTFIELD) instruction; int fieldSize = instr.getFieldType(context.constPool()).getSize(); for (Location l : in.keySet()) { if (l.stackLoc >= 0 && l.stackLoc < context.stackPtr - 1 - fieldSize) { result.put(l, in.get(l)); } } // System.out.println(context.stackPtr+","+fieldSize+": "+result); DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); if (receivers == null) { warnNoReceiver(context, stmt); } else { for (String fieldName : receivers) { String f = fieldName.substring(fieldName.lastIndexOf("."), fieldName.length()); String strippedName; if (fieldName.indexOf("@") >= 0) { strippedName = fieldName.split("@")[0] + f; } else { strippedName = fieldName; } // System.out.println(fieldName+" vs "+strippedName); if (p.containsField(strippedName)) { for (Location l : in.keySet()) { if (l.stackLoc < 0 && !receivers.contains(l.heapLoc)) { result.put(l, in.get(l)); } if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(fieldName), new ValueMapping(in.get(l), false)); } } } } } } break; case Constants.GETFIELD: { GETFIELD instr = (GETFIELD) instruction; filterSet(in, result, context.stackPtr - 1); DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); Location location = new Location(context.stackPtr - 1); boolean valid = false; if (receivers == null) { warnNoReceiver(context, stmt); } else { for (String fieldName : receivers) { String f = fieldName.substring(fieldName.lastIndexOf("."), fieldName.length()); String strippedName; if (fieldName.indexOf("@") >= 0) { strippedName = fieldName.split("@")[0] + f; } else { strippedName = fieldName; } // System.out.println(fieldName+" vs "+strippedName); if (p.containsField(strippedName)) { for (Location l : in.keySet()) { if (l.heapLoc.equals(fieldName)) { ValueMapping value = new ValueMapping(in.get(l), false); value.join(result.get(location)); result.put(location, value); valid = true; } } } } } if (!valid && !(instr.getFieldType(context.constPool()) instanceof ReferenceType)) { result.put(new Location(context.stackPtr - 1), new ValueMapping()); } } break; case Constants.PUTSTATIC: { PUTSTATIC instr = (PUTSTATIC) instruction; int fieldSize = instr.getFieldType(context.constPool()).getSize(); for (Location l : in.keySet()) { if (l.stackLoc >= 0 && l.stackLoc < context.stackPtr - fieldSize) { result.put(l, in.get(l)); } } DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); for (String fieldName : receivers) { if (p.containsField(fieldName)) { for (Location l : in.keySet()) { if (l.stackLoc < 0 && !receivers.contains(l.heapLoc)) { result.put(l, in.get(l)); } if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(fieldName), new ValueMapping(in.get(l), false)); } } } } } break; case Constants.GETSTATIC: { GETSTATIC instr = (GETSTATIC) instruction; result = new LinkedHashMap<Location, ValueMapping>(in); retval.put(context.callString, result); DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); Location location = new Location(context.stackPtr); boolean valid = false; for (String fieldName : receivers) { if (p.containsField(fieldName)) { for (Location l : in.keySet()) { if (l.heapLoc.equals(fieldName)) { ValueMapping value = new ValueMapping(in.get(l), false); value.join(result.get(location)); result.put(location, value); valid = true; } } } } if (!valid && !(instr.getFieldType(context.constPool()) instanceof ReferenceType)) { result.put(new Location(context.stackPtr), new ValueMapping()); } } break; case Constants.IASTORE: case Constants.CASTORE: case Constants.SASTORE: case Constants.BASTORE: { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 3) { result.put(l, in.get(l)); } } DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); if (receivers == null) { warnNoReceiver(context, stmt); break; } for (String name : receivers) { for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { Location loc = new Location(name); ValueMapping val = new ValueMapping(in.get(l), false); val.join(in.get(loc)); result.put(loc, val); } } } } break; case Constants.AASTORE: { filterSet(in, result, context.stackPtr - 3); } break; case Constants.IALOAD: case Constants.CALOAD: case Constants.SALOAD: case Constants.BALOAD: { filterSet(in, result, context.stackPtr - 2); DFATool p = interpreter.getDFATool(); Location location = new Location(context.stackPtr - 2); boolean valid = false; Set<String> receivers = p.getReceivers(stmt, context.callString); for (String name : receivers) { for (Location l : in.keySet()) { if (l.heapLoc.equals(name)) { ValueMapping value = new ValueMapping(in.get(l), false); value.join(result.get(location)); result.put(location, value); valid = true; } } } if (!valid) { result.put(new Location(context.stackPtr - 2), new ValueMapping(0)); } } break; case Constants.AALOAD: { ValueMapping v = in.get(new Location(context.stackPtr - 1)); if (v == null) { logger.warn("no value at: " + context.callString.toStringList() + context.method() + stmt); } else { recordArrayIndex(stmt, context, v.assigned); } filterSet(in, result, context.stackPtr - 2); } break; case Constants.DUP: { for (Location l : in.keySet()) { result.put(l, in.get(l)); if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(context.stackPtr), new ValueMapping(in.get(l), true)); } } } break; case Constants.DUP_X1: { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(context.stackPtr - 2), new ValueMapping(in.get(l), true)); result.put(new Location(context.stackPtr), new ValueMapping(in.get(l), true)); } if (l.stackLoc == context.stackPtr - 2) { result.put(new Location(context.stackPtr - 1), new ValueMapping(in.get(l), true)); } } } break; case Constants.DUP2: { for (Location l : in.keySet()) { result.put(l, in.get(l)); if (l.stackLoc == context.stackPtr - 2) { result.put(new Location(context.stackPtr), new ValueMapping(in.get(l), true)); } if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(context.stackPtr + 1), new ValueMapping(in.get(l), true)); } } } break; case Constants.POP: { filterSet(in, result, context.stackPtr - 1); } break; case Constants.POP2: { filterSet(in, result, context.stackPtr - 2); } break; case Constants.IINC: { IINC instr = (IINC) instruction; int index = instr.getIndex(); int increment = instr.getIncrement(); for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr) { result.put(l, in.get(l)); } if (l.stackLoc == index) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.add(increment); m.constrained.add(increment); Interval operand = new Interval(increment, increment); if (m.increment != null && !m.softinc) { m.increment.join(operand); } else if (m.increment != null && m.softinc) { if ((m.increment.getLb() < 0 && operand.getUb() > 0) || (m.increment.getUb() > 0 && operand.getLb() < 0)) { m.increment.join(operand); } else { m.increment = operand; } m.softinc = false; } else { m.increment = operand; m.softinc = false; } result.put(l, m); } } } break; case Constants.IADD: { Interval operand = new Interval(); for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { operand = in.get(l).assigned; } } for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 2) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.add(operand); m.constrained.add(operand); if (m.increment != null && !m.softinc) { m.increment.join(operand); } else if (m.increment != null && m.softinc) { if ((m.increment.getLb() < 0 && operand.getUb() > 0) || (m.increment.getUb() > 0 && operand.getLb() < 0)) { m.increment.join(operand); } else { m.increment = operand; } m.softinc = false; } else { m.increment = operand; m.softinc = false; } result.put(l, m); } } } break; case Constants.ISUB: { Interval operand = new Interval(); for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { operand = in.get(l).assigned; } } for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 2) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.sub(operand); m.constrained.sub(operand); operand.neg(); // decrement rather than increment if (m.increment != null && !m.softinc) { m.increment.join(operand); } else if (m.increment != null && m.softinc) { if ((m.increment.getLb() < 0 && operand.getUb() > 0) || (m.increment.getUb() > 0 && operand.getLb() < 0)) { m.increment.join(operand); } else { m.increment = operand; } m.softinc = false; } else { m.increment = operand; m.softinc = false; } result.put(l, m); } } } break; case Constants.INEG: { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 1) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.neg(); m.constrained.neg(); m.increment = new Interval(); result.put(l, m); } } } break; case Constants.IUSHR: { Interval operand = new Interval(); for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { operand = in.get(l).assigned; } } for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 2) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.ushr(operand); m.constrained.ushr(operand); m.increment = new Interval(); result.put(l, m); } } } break; case Constants.ISHR: { Interval operand = new Interval(); for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { operand = in.get(l).assigned; } } for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 2) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.shr(operand); m.constrained.shr(operand); m.increment = new Interval(); result.put(l, m); } } } break; case Constants.IMUL: { Interval operand = new Interval(); for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { operand = in.get(l).assigned; } } for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 2) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.mul(operand); m.constrained.mul(operand); m.increment = new Interval(); result.put(l, m); } } } break; case Constants.IDIV: { Interval operand = new Interval(); for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { operand = in.get(l).assigned; } } for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 2) { ValueMapping m = new ValueMapping(in.get(l), true); m.assigned.div(operand); m.constrained.div(operand); m.increment = new Interval(); result.put(l, m); } } } break; case Constants.IAND: case Constants.IOR: case Constants.IXOR: case Constants.IREM: case Constants.ISHL: { // TODO: we could be more clever for some operations for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } else if (l.stackLoc == context.stackPtr - 2) { ValueMapping m = new ValueMapping(); result.put(l, m); } } } break; case Constants.I2B: case Constants.I2C: case Constants.I2S: // TODO: is this really correct? result = in; retval.put(context.callString, result); break; case Constants.MONITORENTER: result = in; retval.put(context.callString, result); context.syncLevel++; break; case Constants.MONITOREXIT: result = in; retval.put(context.callString, result); context.syncLevel--; if (context.syncLevel < 0) { System.err.println("Synchronization level mismatch."); System.exit(-1); } break; case Constants.CHECKCAST: result = in; retval.put(context.callString, result); break; case Constants.INSTANCEOF: { filterSet(in, result, context.stackPtr - 1); ValueMapping bool = new ValueMapping(); bool.assigned.setLb(0); bool.assigned.setUb(1); result.put(new Location(context.stackPtr - 1), bool); } break; case Constants.NEW: { result = in; retval.put(context.callString, result); } break; case Constants.NEWARRAY: { NEWARRAY instr = (NEWARRAY) instruction; String name = instr.getType().toString(); name += "@" + context.method() + ":" + stmt.getPosition(); filterSet(in, result, context.stackPtr - 1); boolean valid = false; for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(name + ".length"), in.get(l)); recordSize(stmt, context, in.get(l).assigned); valid = true; } } if (!valid) { ValueMapping v = new ValueMapping(); result.put(new Location(name + ".length"), v); recordSize(stmt, context, v.assigned); } } break; case Constants.ANEWARRAY: { ANEWARRAY instr = (ANEWARRAY) instruction; String name = instr.getType(context.constPool()).toString() + "[]"; name += "@" + context.method() + ":" + stmt.getPosition(); //System.out.println("NEW ARRAY: "+name); filterSet(in, result, context.stackPtr - 1); boolean valid = false; for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(name + ".length"), in.get(l)); recordSize(stmt, context, in.get(l).assigned); valid = true; } } if (!valid) { ValueMapping v = new ValueMapping(); result.put(new Location(name + ".length"), v); recordSize(stmt, context, v.assigned); } } break; case Constants.MULTIANEWARRAY: { MULTIANEWARRAY instr = (MULTIANEWARRAY) instruction; int dim = instr.getDimensions(); filterSet(in, result, context.stackPtr - dim); String type = instr.getType(context.constPool()).toString(); type = type.substring(0, type.indexOf("[")); Interval[] size = new Interval[dim]; for (int i = 1; i <= dim; i++) { String name = type; for (int k = 0; k < i; k++) { name += "[]"; } name += "@" + context.method() + ":" + stmt.getPosition(); boolean valid = false; for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - i) { result.put(new Location(name + ".length"), in.get(l)); if (size[i - 1] != null) { size[i - 1].join(in.get(l).assigned); } else { size[i - 1] = in.get(l).assigned; } valid = true; } } if (!valid) { ValueMapping v = new ValueMapping(); result.put(new Location(name + ".length"), v); size[i - 1] = v.assigned; } } recordSize(stmt, context, size); } break; case Constants.GOTO: result = in; retval.put(context.callString, result); break; case Constants.IFNULL: case Constants.IFNONNULL: { checkScope(context, stmt); filterSet(in, result, context.stackPtr - 1); } break; case Constants.IF_ACMPEQ: case Constants.IF_ACMPNE: { checkScope(context, stmt); filterSet(in, result, context.stackPtr - 2); } break; case Constants.IFEQ: case Constants.IFNE: case Constants.IFLT: case Constants.IFGE: case Constants.IFLE: case Constants.IFGT: checkScope(context, stmt); result = doIf(stmt, edge, context, in, result); retval.put(context.callString, result); break; case Constants.IF_ICMPEQ: case Constants.IF_ICMPNE: case Constants.IF_ICMPLT: case Constants.IF_ICMPGE: case Constants.IF_ICMPGT: case Constants.IF_ICMPLE: checkScope(context, stmt); result = doIfIcmp(stmt, edge, context, in, result); retval.put(context.callString, result); break; case Constants.LOOKUPSWITCH: case Constants.TABLESWITCH: result = in; retval.put(context.callString, result); break; case Constants.INVOKEVIRTUAL: case Constants.INVOKEINTERFACE: case Constants.INVOKESTATIC: case Constants.INVOKESPECIAL: { DFATool p = interpreter.getDFATool(); Set<String> receivers = p.getReceivers(stmt, context.callString); if (receivers == null) { warnUnknownReceivers(context, stmt); result = in; break; } for (String methodName : receivers) { doInvoke(methodName, stmt, context, input, interpreter, state, retval); } } break; case Constants.ARETURN: case Constants.RETURN: { filterSet(in, result, 0); } break; case Constants.IRETURN: { filterSet(in, result, 0); for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { result.put(new Location(0), new ValueMapping(in.get(l), false)); } } } break; default: // System.out.println("unknown instruction: "+stmt); result = in; retval.put(context.callString, result); break; } // System.out.println(stmt); // System.out.print(stmt.getInstruction()+":\t{ "); // if (retval != null) { // for (Iterator<Map<Location, ValueMapping>> k = retval.values().iterator(); k.hasNext(); ) { // Map<Location, ValueMapping> m = k.next(); // System.out.print(m+", "); // } // } // System.out.println("}"); context.stackPtr += instruction.produceStack(context.constPool()) - instruction.consumeStack(context.constPool()); return retval; } private void warnUnknownReceivers(Context context, InstructionHandle stmt) { Instruction instruction = stmt.getInstruction(); String loc = context.method() + ": invoke " + instruction.toString(context.constPool().getConstantPool()) + "(" + stmt.toString(true) + ")"; if (logger.isTraceEnabled()) { logger.trace(loc + " unknown receivers"); } if (unknownReceiverWarnings.add(loc)) { logger.warn(loc + " unknown receivers"); } } private void warnNoReceiver(Context context, InstructionHandle stmt) { String loc = context.callString.toStringList() + context.method() + stmt; if (logger.isTraceEnabled()) { logger.trace("no receivers at: " + loc); } if (noReceiverWarnings.add(loc)) { logger.warn("no receivers at: " + loc); } } private void recordArrayIndex(InstructionHandle stmt, Context context, Interval assigned) { ContextMap<CallString, Interval> indexMap = arrayIndices.get(stmt); if (indexMap == null) { indexMap = new ContextMap<CallString, Interval>(context, new LinkedHashMap<CallString, Interval>()); arrayIndices.put(stmt, indexMap); } indexMap.put(context.callString, assigned); } private void filterSet(Map<Location, ValueMapping> in, Map<Location, ValueMapping> result, int bound) { for (Location l : in.keySet()) { if (l.stackLoc < bound) { result.put(l, in.get(l)); } } } private void checkScope(Context context, InstructionHandle stmt) { if (scopes.get(stmt) == null) { scopes.put(stmt, new ContextMap<CallString, Integer>(context, new LinkedHashMap<CallString, Integer>())); } if (scopes.get(stmt).get(context.callString) == null) { ValueMapping.scope = ++ValueMapping.scopeCnt; scopes.get(stmt).put(context.callString, ValueMapping.scope); } } private Map<Location, ValueMapping> doIf(InstructionHandle stmt, FlowEdge edge, Context context, Map<Location, ValueMapping> in, Map<Location, ValueMapping> result) { // copy input values for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1) { result.put(l, in.get(l)); } } // apply constraint loop: for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { ValueMapping m = new ValueMapping(in.get(l), true); switch (stmt.getInstruction().getOpcode()) { case Constants.IFEQ: if (edge.getType() == FlowEdge.FALSE_EDGE) { // != 0 cannot be expressed as interval // but we can check infeasibility if (m.assigned.getUb() == 0 && m.assigned.getLb() == 0) { recordInfeasible(stmt, context, edge); result = null; break loop; } } else if (edge.getType() == FlowEdge.TRUE_EDGE) { m.constrained.setLb(0); m.constrained.setUb(0); } break; case Constants.IFNE: if (edge.getType() == FlowEdge.FALSE_EDGE) { m.constrained.setLb(0); m.constrained.setUb(0); } else if (edge.getType() == FlowEdge.TRUE_EDGE) { // != 0 cannot be expressed as interval // but we can check infeasibility if (m.assigned.getUb() == 0 && m.assigned.getLb() == 0) { recordInfeasible(stmt, context, edge); result = null; break loop; } } break; case Constants.IFLT: if (edge.getType() == FlowEdge.FALSE_EDGE) { m.constrained.setLb(0); } else if (edge.getType() == FlowEdge.TRUE_EDGE) { m.constrained.setUb(-1); } break; case Constants.IFGE: if (edge.getType() == FlowEdge.FALSE_EDGE) { m.constrained.setUb(-1); } else if (edge.getType() == FlowEdge.TRUE_EDGE) { m.constrained.setLb(0); } break; case Constants.IFLE: if (edge.getType() == FlowEdge.FALSE_EDGE) { m.constrained.setLb(1); } else if (edge.getType() == FlowEdge.TRUE_EDGE) { m.constrained.setUb(0); } break; case Constants.IFGT: if (edge.getType() == FlowEdge.FALSE_EDGE) { m.constrained.setUb(0); } else if (edge.getType() == FlowEdge.TRUE_EDGE) { m.constrained.setLb(1); } break; } if (m.assigned.getLb() > m.constrained.getUb() || m.assigned.getUb() < m.constrained.getLb()) { recordInfeasible(stmt, context, edge); result = null; break; } removeInfeasible(stmt, context, edge); m.assigned.constrain(m.constrained); recordBound(stmt, context, edge, new ValueMapping(m, true)); m.softinc = true; // TODO: is this really correct for all cases? if (in.get(l).source != null) { result.put(in.get(l).source, m); } } } return result; } private Map<Location, ValueMapping> doIfIcmp(InstructionHandle stmt, FlowEdge edge, Context context, Map<Location, ValueMapping> in, Map<Location, ValueMapping> result) { // search for constraining value Interval constraint = null; for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 1) { constraint = in.get(l).assigned; } } // copy input values for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { result.put(l, in.get(l)); } } // apply constraint for (Location l : in.keySet()) { if (l.stackLoc == context.stackPtr - 2 && in.get(l).source != null) { ValueMapping m = new ValueMapping(in.get(l), true); if (constraint != null) { switch (stmt.getInstruction().getOpcode()) { case Constants.IF_ICMPEQ: if (edge.getType() == FlowEdge.FALSE_EDGE) { // != Interval not expressable as Interval // TODO: mark paths infeasible if appropriate } else if (edge.getType() == FlowEdge.TRUE_EDGE) { if (constraint.hasLb()) { m.constrained.setLb(constraint.getLb()); } if (constraint.hasUb()) { m.constrained.setUb(constraint.getUb()); } } break; case Constants.IF_ICMPNE: if (edge.getType() == FlowEdge.FALSE_EDGE) { if (constraint.hasLb()) { m.constrained.setLb(constraint.getLb()); } if (constraint.hasUb()) { m.constrained.setUb(constraint.getUb()); } } else if (edge.getType() == FlowEdge.TRUE_EDGE) { // != Interval not expressable as Interval // TODO: mark paths infeasible if appropriate } break; case Constants.IF_ICMPLT: if (edge.getType() == FlowEdge.FALSE_EDGE) { if (constraint.hasLb()) { m.constrained.setLb(constraint.getLb()); } } else if (edge.getType() == FlowEdge.TRUE_EDGE) { if (constraint.hasUb()) { m.constrained.setUb(constraint.getUb() - 1); } } break; case Constants.IF_ICMPGE: if (edge.getType() == FlowEdge.FALSE_EDGE) { if (constraint.hasUb()) { m.constrained.setUb(constraint.getUb() - 1); } } else if (edge.getType() == FlowEdge.TRUE_EDGE) { if (constraint.hasLb()) { m.constrained.setLb(constraint.getLb()); } } break; case Constants.IF_ICMPGT: if (edge.getType() == FlowEdge.FALSE_EDGE) { if (constraint.hasUb()) { m.constrained.setUb(constraint.getUb()); } } else if (edge.getType() == FlowEdge.TRUE_EDGE) { if (constraint.hasLb()) { m.constrained.setLb(constraint.getLb() + 1); } } break; case Constants.IF_ICMPLE: if (edge.getType() == FlowEdge.FALSE_EDGE) { if (constraint.hasLb()) { m.constrained.setLb(constraint.getLb() - 1); } } else if (edge.getType() == FlowEdge.TRUE_EDGE) { if (constraint.hasUb()) { m.constrained.setUb(constraint.getUb()); } } break; } } if (m.assigned.getLb() > m.constrained.getUb() || m.assigned.getUb() < m.constrained.getLb()) { recordInfeasible(stmt, context, edge); result = null; break; } removeInfeasible(stmt, context, edge); m.assigned.constrain(m.constrained); recordBound(stmt, context, edge, new ValueMapping(m, true)); m.softinc = true; // TODO: is this really correct for all cases? result.put(in.get(l).source, m); } } return result; } private void doInvoke(String methodName, InstructionHandle stmt, Context context, Map<CallString, Map<Location, ValueMapping>> input, Interpreter<CallString, Map<Location, ValueMapping>> interpreter, Map<InstructionHandle, ContextMap<CallString, Map<Location, ValueMapping>>> state, Map<CallString, Map<Location, ValueMapping>> result) { DFATool p = interpreter.getDFATool(); MethodInfo method = p.getMethod(methodName); //methodName = method.getMemberID().toString(); // System.out.println(context.callString.asList()+"/"+context.method+": "+stmt+" invokes method: "+methodName); if (method.isNative()) { handleNative(method, context, input, result); } else { // set up new context int varPtr = context.stackPtr - MethodHelper.getArgSize(method); Context c = new Context(context); c.stackPtr = method.getCode().getMaxLocals(); if (method.isSynchronized()) { c.syncLevel = context.syncLevel + 1; } c.setMethodInfo(method); c.callString = c.callString.push(method, stmt, callStringLength); // carry only minimal information with call Map<Location, ValueMapping> in = input.get(context.callString); Map<Location, ValueMapping> out = new LinkedHashMap<Location, ValueMapping>(); for (Location l : in.keySet()) { if (l.stackLoc < 0) { out.put(l, in.get(l)); } if (l.stackLoc >= varPtr) { out.put(new Location(l.stackLoc - varPtr), new ValueMapping(in.get(l), false)); } } ContextMap<CallString, Map<Location, ValueMapping>> tmpresult = new ContextMap<CallString, Map<Location, ValueMapping>>(c, new LinkedHashMap<CallString, Map<Location, ValueMapping>>()); tmpresult.put(c.callString, out); InstructionHandle entry = p.getEntryHandle(method); state.put(entry, join(state.get(entry), tmpresult)); // interpret method Map<InstructionHandle, ContextMap<CallString, Map<Location, ValueMapping>>> r = interpreter.interpret(c, entry, state, false); //System.out.println(">>>>>>>>"); // pull out relevant information from call InstructionHandle exit = p.getExitHandle(method); if (r.get(exit) != null) { Map<Location, ValueMapping> returned = r.get(exit).get(c.callString); if (returned != null) { for (Location l : returned.keySet()) { if (l.stackLoc < 0) { ValueMapping m = new ValueMapping(returned.get(l), true); m.join(result.get(context.callString).get(l)); result.get(context.callString).put(l, m); } if (l.stackLoc >= 0) { ValueMapping m = new ValueMapping(returned.get(l), false); Location loc = new Location(l.stackLoc + varPtr); m.join(result.get(context.callString).get(loc)); result.get(context.callString).put(loc, m); } } } } // add relevant information to result for (Location l : in.keySet()) { if (l.stackLoc >= 0 && l.stackLoc < context.stackPtr - MethodHelper.getArgSize(method)) { result.get(context.callString).put(l, new ValueMapping(in.get(l), true)); } } } } @SuppressWarnings({"LiteralAsArgToStringEquals"}) private Map<CallString, Map<Location, ValueMapping>> handleNative(MethodInfo method, Context context, Map<CallString, Map<Location, ValueMapping>> input, Map<CallString, Map<Location, ValueMapping>> result) { String methodId = method.getMemberID().toString(); Map<Location, ValueMapping> in = input.get(context.callString); Map<Location, ValueMapping> out = new LinkedHashMap<Location, ValueMapping>(); if (methodId.equals("com.jopdesign.sys.Native#rd(I)I") || methodId.equals("com.jopdesign.sys.Native#rdMem(I)I") || methodId.equals("com.jopdesign.sys.Native#rdIntMem(I)I") || methodId.equals("com.jopdesign.sys.Native#getStatic(I)I")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1) { out.put(l, in.get(l)); } } out.put(new Location(context.stackPtr - 1), new ValueMapping()); } else if (methodId.equals("com.jopdesign.sys.Native#wr(II)V") || methodId.equals("com.jopdesign.sys.Native#wrMem(II)V") || methodId.equals("com.jopdesign.sys.Native#wrIntMem(II)V") || methodId.equals("com.jopdesign.sys.Native#putStatic(II)V") || methodId.equals("com.jopdesign.sys.Native#toLong(D)J") || methodId.equals("com.jopdesign.sys.Native#toDouble(J)D")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { out.put(l, in.get(l)); } } } else if (methodId.equals("com.jopdesign.sys.Native#toInt(Ljava/lang/Object;)I") || methodId.equals("com.jopdesign.sys.Native#toInt(F)I")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1) { out.put(l, in.get(l)); } } out.put(new Location(context.stackPtr - 1), new ValueMapping()); } else if (methodId.equals("com.jopdesign.sys.Native#toObject(I)Ljava/lang/Object;") || methodId.equals("com.jopdesign.sys.Native#toIntArray(I)[I") || methodId.equals("com.jopdesign.sys.Native#toFloat(I)F")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1) { out.put(l, in.get(l)); } } } else if (methodId.equals("com.jopdesign.sys.Native#getSP()I")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr) { out.put(l, in.get(l)); } } out.put(new Location(context.stackPtr), new ValueMapping()); } else if (methodId.equals("com.jopdesign.sys.Native#toInt(Ljava/lang/Object;)I")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1) { out.put(l, in.get(l)); } } out.put(new Location(context.stackPtr - 1), new ValueMapping()); } else if (methodId.equals("com.jopdesign.sys.Native#condMove(IIZ)I") || methodId.equals("com.jopdesign.sys.Native#condMoveRef(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;") || methodId.equals("com.jopdesign.sys.Native#memCopy(III)V")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 3) { out.put(l, in.get(l)); } } } else if (methodId.equals("com.jopdesign.sys.Native#invalidate()V")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr) { out.put(l, in.get(l)); } } } else if (methodId.equals("com.jopdesign.sys.Native#arrayLength(I)I") || methodId.equals("com.jopdesign.sys.Native#invoke(I)V")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 1) { out.put(l, in.get(l)); } } } else if (methodId.equals("com.jopdesign.sys.Native#invoke(II)V")) { for (Location l : in.keySet()) { if (l.stackLoc < context.stackPtr - 2) { out.put(l, in.get(l)); } } } else { AppInfoError ex = new AppInfoError("Unknown native method: " + methodId); logger.error(ex); throw ex; } result.put(context.callString, out); return result; } private void recordBound(InstructionHandle stmt, Context context, FlowEdge edge, ValueMapping bound) { ContextMap<CallString, Pair<ValueMapping, ValueMapping>> map = bounds.get(stmt); if (map == null) { map = new ContextMap<CallString, Pair<ValueMapping, ValueMapping>>(context, new LinkedHashMap<CallString, Pair<ValueMapping, ValueMapping>>()); bounds.put(stmt, map); } Pair<ValueMapping, ValueMapping> b = map.get(context.callString); if (b == null) { b = new Pair<ValueMapping, ValueMapping>(); map.put(context.callString, b); } // System.out.println("CONDITION BOUND: "+bound); // System.out.println("\tin "+context.callString+"/"+context.method); if (edge.getType() == FlowEdge.FALSE_EDGE) { map.put(context.callString, new Pair<ValueMapping, ValueMapping>(b.first(), bound)); } else if (edge.getType() == FlowEdge.TRUE_EDGE) { map.put(context.callString, new Pair<ValueMapping, ValueMapping>(bound, b.second())); } } public Map<InstructionHandle, ContextMap<CallString, Pair<ValueMapping, ValueMapping>>> getResult() { return bounds; } public int getBound(InstructionHandle instr) { return getBound(instr, CallString.EMPTY); } public int getBound(InstructionHandle instr, CallString csSuffix) { ContextMap<CallString, Pair<ValueMapping, ValueMapping>> r = bounds.get(instr); if (r == null) { // no bound at this point return -1; } // merge bound for all contexts int maxValue = -1; for (CallString callString : r.keySet()) { if (!callString.hasSuffix(csSuffix)) continue; Pair<ValueMapping, ValueMapping> bounds = r.get(callString); ValueMapping first = bounds.first(); ValueMapping second = bounds.second(); if (first == null || second == null) { return -1; } InstructionHandle target = ((BranchInstruction) instr.getInstruction()).getTarget(); if (scopes.get(target) != null) { if (scopes.get(target).get(callString) <= first.defscope || scopes.get(target).get(callString) <= second.defscope) { return -1; } } if (scopes.get(instr).get(callString) <= first.defscope || scopes.get(instr).get(callString) <= second.defscope) { return -1; } // if (first.softinc || second.softinc) { // return -1; // } int val = ValueMapping.computeBound(first, second); if (val < 0) { // no bound for some context return -1; } else { // compute the maximum maxValue = Math.max(maxValue, val); } } return maxValue; } private void recordSize(InstructionHandle stmt, Context context, Interval size) { ContextMap<CallString, Interval[]> sizeMap; sizeMap = sizes.get(stmt); if (sizeMap == null) { sizeMap = new ContextMap<CallString, Interval[]>(context, new LinkedHashMap<CallString, Interval[]>()); } Interval[] v = new Interval[1]; v[0] = size; sizeMap.put(context.callString, v); sizes.put(stmt, sizeMap); } private void recordSize(InstructionHandle stmt, Context context, Interval[] size) { ContextMap<CallString, Interval[]> sizeMap; sizeMap = sizes.get(stmt); if (sizeMap == null) { sizeMap = new ContextMap<CallString, Interval[]>(context, new LinkedHashMap<CallString, Interval[]>()); } sizeMap.put(context.callString, size); sizes.put(stmt, sizeMap); } public void printSizeResult(DFATool program) { for (InstructionHandle instr : sizes.keySet()) { ContextMap<CallString, Interval[]> r = sizes.get(instr); Context c = r.getContext(); LineNumberTable lines = c.getMethodInfo().getCode().getLineNumberTable(); int sourceLine = lines.getSourceLine(instr.getPosition()); for (CallString callString : r.keySet()) { Interval[] bounds = r.get(callString); System.out.println(c.method() + ":" + sourceLine + ":\t" + callString.toStringList() + ": "); System.out.println(Arrays.asList(bounds)); } } } private void recordInfeasible(InstructionHandle stmt, Context context, FlowEdge edge) { ContextMap<CallString, Set<FlowEdge>> infMap; infMap = infeasibles.get(stmt); if (infMap == null) { infMap = new ContextMap<CallString, Set<FlowEdge>>(context, new LinkedHashMap<CallString, Set<FlowEdge>>()); infeasibles.put(stmt, infMap); } Set<FlowEdge> flowSet = infMap.get(context.callString); if (flowSet == null) { flowSet = new LinkedHashSet<FlowEdge>(); infMap.put(context.callString, flowSet); } flowSet.add(edge); } private void removeInfeasible(InstructionHandle stmt, Context context, FlowEdge edge) { ContextMap<CallString, Set<FlowEdge>> infMap; infMap = infeasibles.get(stmt); if (infMap == null) { return; } Set<FlowEdge> flowSet = infMap.get(context.callString); if (flowSet == null) { return; } flowSet.remove(edge); } public void printInfeasibles(DFATool program) { System.out.println(infeasibles); } public Set<FlowEdge> getInfeasibleEdges(InstructionHandle stmt, CallString cs) { ContextMap<CallString, Set<FlowEdge>> map = infeasibles.get(stmt); Set<FlowEdge> retval = new LinkedHashSet<FlowEdge>(); if (map == null) { // no infeasibility information here return retval; } for (CallString c : map.keySet()) { if (c.hasSuffix(cs)) { Set<FlowEdge> f = map.get(c); retval.addAll(f); } } return retval; } public Interval[] getArraySizes(InstructionHandle stmt, CallString cs) { ContextMap<CallString, Interval[]> map = sizes.get(stmt); Interval[] retval = null; if (map == null) { return retval; } for (CallString c : map.keySet()) { if (c.hasSuffix(cs)) { if (retval == null) { Interval[] v = map.get(c); // JDK1.5 compat retval = new Interval[v.length]; System.arraycopy(v, 0, retval, 0, v.length); // JDK1.6 only //retval = Arrays.copyOf(v, v.length); } else { Interval[] v = map.get(c); for (int k = 0; k < v.length; k++) { retval[k].join(v[k]); } } } } return retval; } public Interval getArrayIndices(InstructionHandle stmt, CallString cs) { ContextMap<CallString, Interval> map = arrayIndices.get(stmt); Interval retval = null; if (map == null) { return retval; } for (CallString c : map.keySet()) { if (c.hasSuffix(cs)) { if (retval == null) { retval = new Interval(map.get(c)); } else { retval.join(map.get(c)); } } } return retval; } private class LoopBoundsResultFormatter implements ResultFormatter<Pair<ValueMapping,ValueMapping>> { private AppInfo appInfo; public LoopBoundsResultFormatter(AppInfo appInfo) { this.appInfo = appInfo; } @Override public String format(String method, CallStringSerialization css, Integer position, Pair<ValueMapping, ValueMapping> bounds) { StringBuffer sb = new StringBuffer(); ValueMapping first = bounds.first(); ValueMapping second = bounds.second(); sb.append("[true: "); sb.append(first); sb.append(", false: "); sb.append(second); sb.append(", bound: "); InstructionHandle instr; CallString cs; try { instr = appInfo.getMethodInfo(MemberID.parse(method)). getCode().getInstructionList(false, false).findHandle(position); cs = css.getCallString(appInfo); int val = getBound(instr, cs); if (val >= 0) { sb.append(val); } else { sb.append("invalid"); } } catch (MethodNotFoundException e) { e.printStackTrace(); } return sb.toString(); } } public void printResult(DFATool program) { AnalysisResultSerialization<Pair<ValueMapping,ValueMapping>> serializedResult = AnalysisResultSerialization.fromContextMapResult(getResult()); serializedResult.dump(System.out, new LoopBoundsResultFormatter(program.getAppInfo())); System.out.println("=== Array Indices ==="); AnalysisResultSerialization.fromContextMapResult(arrayIndices).dump(System.out); System.out.println("=== Infeasibles ==="); AnalysisResultSerialization.fromContextMapResult(infeasibles, FLOW_EDGE_SET_CONVERTER).dump(System.out); System.out.println("=== Sizes ==="); AnalysisResultSerialization.fromContextMapResult(sizes).dump(System.out); } public static final Serializer<Set<FlowEdge>,List<SerializedFlowEdge>> FLOW_EDGE_SET_CONVERTER = new Serializer<Set<FlowEdge>, List<SerializedFlowEdge>>() { @Override public Set<FlowEdge> fromSerializedRepresentation( List<SerializedFlowEdge> serializedEdges, AppInfo appInfo) throws IOException, MethodNotFoundException { Set<FlowEdge> edges = new LinkedHashSet<FlowEdge>(); for(SerializedFlowEdge sfe : serializedEdges) { edges.add(sfe.toFlowEdge(appInfo)); } return edges; } @Override public List<SerializedFlowEdge> serializedRepresentation( Set<FlowEdge> edges) { List<SerializedFlowEdge> serializedEdges = new ArrayList<SerializedFlowEdge>(); for(FlowEdge fe : edges) { if (!SerializedFlowEdge.exists(fe)) continue; serializedEdges.add(new FlowEdge.SerializedFlowEdge(fe)); } return serializedEdges; } }; /* SERIALIZE (in order): bounds, arrayIndices, infeasibles, scopes, sizes */ @Override public void serializeResult(File cacheFile) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(cacheFile)); AnalysisResultSerialization.fromContextMapResult(bounds).serialize(oos); AnalysisResultSerialization.fromContextMapResult(arrayIndices).serialize(oos); AnalysisResultSerialization.fromContextMapResult(infeasibles, FLOW_EDGE_SET_CONVERTER).serialize(oos); AnalysisResultSerialization.fromContextMapResult(scopes).serialize(oos); AnalysisResultSerialization.fromContextMapResult(sizes).serialize(oos); oos.close(); } /* DESERIALIZE (in order): bounds, arrayIndices, infeasibles, scopes, sizes */ public Map deSerializeResult(AppInfo appInfo, File cacheFile) throws IOException, ClassNotFoundException, MethodNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(cacheFile)); bounds = AnalysisResultSerialization.deserializeContextMap(appInfo, ois, null); arrayIndices = AnalysisResultSerialization.deserializeContextMap(appInfo, ois, null); infeasibles = AnalysisResultSerialization.deserializeContextMap(appInfo, ois, FLOW_EDGE_SET_CONVERTER); scopes = AnalysisResultSerialization.deserializeContextMap(appInfo, ois, null); sizes = AnalysisResultSerialization.deserializeContextMap(appInfo, ois, null); ois.close(); return this.getResult(); } @Override public void copyResults(MethodInfo newContainer, Map<InstructionHandle, InstructionHandle> newHandles) { for (Map.Entry<InstructionHandle,InstructionHandle> entry : newHandles.entrySet()) { InstructionHandle oldHandle = entry.getKey(); InstructionHandle newHandle = entry.getValue(); if (newHandle == null) continue; // TODO support updating the callstrings too // TODO this does NOT update stackPtr,.. in the new context! ContextMap<CallString, Pair<ValueMapping, ValueMapping>> value = bounds.get(oldHandle); if (value != null) bounds.put(newHandle, value.copy(newContainer)); ContextMap<CallString, Interval> value1 = arrayIndices.get(oldHandle); if (value1 != null) arrayIndices.put(newHandle, value1.copy(newContainer)); ContextMap<CallString, Integer> value2 = scopes.get(oldHandle); if (value2 != null) scopes.put(newHandle, value2.copy(newContainer)); ContextMap<CallString, Interval[]> value3 = sizes.get(oldHandle); if (value3 != null) sizes.put(newHandle, value3.copy(newContainer)); ContextMap<CallString,Set<FlowEdge>> old = infeasibles.get(oldHandle); if (old != null) { Map<CallString,Set<FlowEdge>> map = new LinkedHashMap<CallString, Set<FlowEdge>>(old.size()); for (CallString cs : old.keySet()) { Set<FlowEdge> newSet = new LinkedHashSet<FlowEdge>(); for (FlowEdge edge : old.get(cs)) { InstructionHandle newHead = newHandles.get(edge.getHead()); InstructionHandle newTail = newHandles.get(edge.getTail()); if (newHead != null && newTail != null) { Context ctx = new Context(edge.getContext()); ctx.setMethodInfo(newContainer); newSet.add(new FlowEdge(newTail, newHead, edge.getType(), ctx)); } } map.put(cs,newSet); } Context c = old.getContext(); c.setMethodInfo(newContainer); ContextMap<CallString,Set<FlowEdge>> edges = new ContextMap<CallString, Set<FlowEdge>>(c, map); infeasibles.put(newHandle, edges); } } } }