/****************************************************************************** * Copyright (c) 2009 - 2015 IBM Corporation. * 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 *****************************************************************************/ /** * */ package com.ibm.wala.memsat.viz; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.propagation.AbstractFieldPointerKey; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; import com.ibm.wala.ipa.modref.ArrayLengthKey; import com.ibm.wala.memsat.frontEnd.FieldSSATable; import com.ibm.wala.memsat.frontEnd.IRType; import com.ibm.wala.memsat.frontEnd.WalaCGNodeInformation; import com.ibm.wala.memsat.frontEnd.WalaInformation; import com.ibm.wala.memsat.representation.ArrayExpression; import com.ibm.wala.memsat.representation.ExpressionFactory; import com.ibm.wala.memsat.representation.FieldExpression; import com.ibm.wala.memsat.representation.HeapExpression; import com.ibm.wala.memsat.representation.Interpreter; import com.ibm.wala.memsat.translation.Environment.Frame; import com.ibm.wala.memsat.translation.sequential.SequentialTranslation; import kodkod.ast.Expression; import kodkod.engine.Evaluator; import kodkod.engine.Solution; import kodkod.util.ints.SparseSequence; /** * Visualizes the results of a sequential translation as a string. * * @author etorlak */ final class SequentialStringVisualizer extends StringVisualizer<SequentialTranslation> { private final WalaInformation info; private final ExpressionFactory factory; private final CGNode node; /** * Constructs a new string visualizer from the given translation and solution. * @requires solution.instance!=null * @requires solution.formula = translation.formula && solution.bounds = translation.bounds */ SequentialStringVisualizer(SequentialTranslation translation, Solution solution) { super(translation, solution); this.factory = translation.factory(); this.info = factory.info(); this.node = info.threads().iterator().next(); } /** * {@inheritDoc} * @see com.ibm.wala.memsat.viz.StringVisualizer#visualize() */ public String visualize() { final StringBuilder str = new StringBuilder(); warnings(str, translation.context().warnings()); str.append("solution interpretation:\n"); method(str); arguments(str); returnValue(str); exceptionValue(str); if (info.relevantFields().isEmpty()) { str.append(" initial heap: (empty)\n"); str.append(" final heap: (empty)\n"); } else { final Map<InstanceKey, Set<PointerKey>> heapShape = collate(); initialHeap(str, heapShape); finalHeap(str, heapShape); } return str.toString(); } /** Appends the name of this.node to the given builder, followed by a newline. */ private final void method(StringBuilder str) { str.append(" method: " + node.getMethod() + "\n"); } /** Appends the arguments of this.node to the given builder, followed by a newline. */ private final void arguments(StringBuilder str) { str.append(" arguments: ("); int params = node.getMethod().getNumberOfParameters(); final WalaCGNodeInformation nodeInfo = info.cgNodeInformation(node); final Object[] paramTransls = translation.factory().arguments(node); for(int param = 1; param <= params; param++) { final IRType paramType = nodeInfo.typeOf(param); final Object paramTransl = paramTransls[param-1]; str.append(factory.constants().interpreter(paramType).evaluate(paramTransl, eval)); if (param < params) str.append(", "); } str.append(")\n"); } /** Appends the return value of this.node to the given builder, followed by a newline. */ private final void returnValue(StringBuilder str) { str.append(" return value: "); final Object retTransl = translation.context().returnValue(); if (retTransl==null) str.append("(void)"); else { final IRType retType = IRType.convert(node.getMethod().getReturnType()); str.append(factory.constants().interpreter(retType).evaluate(retTransl,eval)); } str.append("\n"); } /** Appends the exception value of this.node to the given builder, followed by a newline. */ private final void exceptionValue(StringBuilder str) { str.append(" exception value: "); final Object exceptionTransl = translation.context().exceptionValue(); if (exceptionTransl==null) str.append("(none)"); else { str.append(factory.constants().interpreter(IRType.OBJECT).evaluate(exceptionTransl,eval)); } str.append("\n"); } /** * Appends the initial heap values of this.node to the given builder, followed by a newline. * @requires heapShape.equals(this.collate()) **/ private final void initialHeap(StringBuilder str, Map<InstanceKey,Set<PointerKey>> heapShape) { final Frame frame = translation.context().frame(); final FieldSSATable ssa = info.cgNodeInformation(node).fieldSSA(); str.append(" initial heap:"); for(Map.Entry<InstanceKey,Set<PointerKey>> e: heapShape.entrySet()) { final Set<PointerKey> fields = e.getValue(); for(Iterator<Expression> instances = e.getKey()!=null?factory.constants().instances(e.getKey()): Collections.singleton((Expression)null).iterator(); instances.hasNext(); ) { final Expression instance = instances.next(); for(PointerKey field : fields) { str.append("\n " + fieldName(instance,field) + "="); HeapExpression<?> fieldTransl = frame.heapUse(ssa.getEntryValue(field)); if (fieldTransl.isArray()) { final int length = ((FieldExpression<?>) frame.heapUse(ssa.getEntryValue(lengthKey(fields)))).evaluate(instance, eval); final Object defaultValue = defaultValue(fieldTransl.valueInterpreter(),eval); str.append(toArray(((ArrayExpression<?>)fieldTransl).evaluate(instance, eval), length, defaultValue)); } else { str.append(""+((FieldExpression<?>)fieldTransl).evaluate(instance, eval)); } } } } str.append("\n"); } /** * Appends the final heap values of this.node to the given builder, followed by a newline. * @requires heapShape.equals(this.collate()) **/ private final void finalHeap(StringBuilder str, Map<InstanceKey,Set<PointerKey>> heapShape) { final Frame frame = translation.context().frame(); final FieldSSATable ssa = info.cgNodeInformation(node).fieldSSA(); str.append(" final heap:"); for(Map.Entry<InstanceKey,Set<PointerKey>> e: heapShape.entrySet()) { Set<PointerKey> fields = e.getValue(); for(Iterator<Expression> instances = e.getKey()!=null?factory.constants().instances(e.getKey()): Collections.singleton((Expression)null).iterator(); instances.hasNext(); ) { final Expression instance = instances.next(); for(PointerKey field : fields) { str.append("\n " + fieldName(instance,field) + "="); final HeapExpression<?> fieldTransl = frame.heapUse(ssa.getExitValue(field)); if (fieldTransl.isArray()) { final int length = ((FieldExpression<?>) frame.heapUse(ssa.getExitValue(lengthKey(fields)))).evaluate(instance, eval); final Object defaultValue = defaultValue(fieldTransl.valueInterpreter(),eval); str.append(toArray(((ArrayExpression<?>)fieldTransl).evaluate(instance, eval), length, defaultValue)); } else { str.append(""+((FieldExpression<?>)fieldTransl).evaluate(instance, eval)); } } } } str.append("\n"); } private static String fieldName(Expression instance, PointerKey field) { return (instance==null?"":(instance + ".")) + field; } /** * Returns a dense array representation from the given sparse representation, where * the length of the returned array and the default value are as specified. * @return a dense array representation from the given sparse representation, where * the length of the returned array and the default value are as specified. */ private static <V> List<V> toArray(SparseSequence<V> s, int length, V defaultValue) { final List<V> list = new ArrayList<V>(length); for(int i = 0; i < length; i++) { list.add( s.containsIndex(i) ? s.get(i) : defaultValue ); } return list; } /** * Returns the value of the interpreter.defaultValue(), as given by the specified evaluator. * @return the value of the interpreter.defaultValue(), as given by the specified evaluator. */ private static <T> Object defaultValue(Interpreter<T> interpreter, Evaluator eval) { return interpreter.evaluate(interpreter.defaultValue(), eval); } /** * Returns the first ArrayLengthKey found in the given set or null if none is found. * @return the first ArrayLengthKey found in the given set or null if none is found. */ private static ArrayLengthKey lengthKey(Set<PointerKey> keys) { for(PointerKey key : keys) { if (key instanceof ArrayLengthKey) { return (ArrayLengthKey)key; } } return null; } /** * Returns a map from each instance key in info.relevantClasses to the * fields in relevant fields for which they are the source. * @return a map, as described above. */ private Map<InstanceKey, Set<PointerKey>> collate() { final Map<InstanceKey, Set<PointerKey>> ret = new LinkedHashMap<InstanceKey, Set<PointerKey>>(); for(InstanceKey key: info.relevantClasses()) { ret.put(key, new LinkedHashSet<PointerKey>()); } ret.put(null, new LinkedHashSet<PointerKey>()); for(PointerKey field : info.relevantFields()) { if (field instanceof AbstractFieldPointerKey){ AbstractFieldPointerKey afield = (AbstractFieldPointerKey) field; ret.get(afield.getInstanceKey()).add(afield); } else { ret.get(null).add(field); } } return ret; } }