/******************************************************************************
* 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.translation;
import static com.ibm.wala.types.TypeReference.JavaLangObject;
import static com.ibm.wala.types.TypeReference.JavaLangString;
import static com.ibm.wala.types.TypeReference.JavaLangSystem;
import static com.ibm.wala.types.TypeReference.JavaLangThrowable;
import static kodkod.ast.Formula.FALSE;
import static kodkod.ast.Formula.TRUE;
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.cast.java.ipa.callgraph.JavaSourceAnalysisScope;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.ipa.callgraph.propagation.ArrayContentsKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceFieldKey;
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.Options;
import com.ibm.wala.memsat.frontEnd.FieldSSATable;
import com.ibm.wala.memsat.frontEnd.IRType;
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.PhiExpression;
import com.ibm.wala.memsat.translation.Environment.Frame;
import com.ibm.wala.types.Descriptor;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.strings.Atom;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.IntExpression;
/**
* Provides access to specialized translators that translate methods according to
* their specification rather than actual code.
*
* @author Emina Torlak
*/
final class SpecialTranslatorFactory {
private final Map<MethodReference,MethodTranslator> transls;
private SpecialTranslatorFactory(Set<MethodReference> empties) {
transls = translators(empties);
}
/**
* Returns a factory of special translators created using the given {@linkplain Options} and {@linkplain WalaInformation}.
* @return factory of special translators created using the given {@linkplain Options} and {@linkplain WalaInformation}.
*/
@SuppressWarnings("unchecked")
public static SpecialTranslatorFactory factory(Options options, WalaInformation info) {
// if (info.threads().getNumberOfNodes()>1) {
// return new SpecialTranslatorFactory(options.memoryModel().memoryInstructions());
// } else {
return new SpecialTranslatorFactory(Collections.EMPTY_SET);
// }
}
/**
* Returns a translator for the given method reference, if it is a special method,
* or null otherwise.
* @return a translator for the given method reference, if it is a special method,
* or null otherwise.
*/
public MethodTranslator translatorFor(MethodReference m) { return transls.get(m); }
/**
* Returns true if this factory has a translator for the given method.
* @return true if this factory has a translator for the given method.
*/
public boolean hasTranslatorFor(MethodReference m) { return transls.containsKey(m); }
/**
* Returns a map of available specification-based method translators.
* All methods in the given set are mapped to unconditionally executed,
* no-body translators.
* @requires all m: empties.elements | m.getReturnType = TypeReference.Void
* @return a map that maps method references to their corresponding
* specification-based translators translators
**/
private static Map<MethodReference,MethodTranslator> translators(Set<MethodReference> empties) {
final Map<MethodReference,MethodTranslator> translators =
new LinkedHashMap<MethodReference, MethodTranslator>();
final MethodTranslator emptyTrue = emptyTranslator(TRUE);
final MethodTranslator emptyFalse = emptyTranslator(FALSE);
// empties
for(MethodReference m : empties) {
if (!TypeReference.Void.equals(m.getReturnType())) {
throw new IllegalArgumentException("Special user methods cannot return any values: " + m);
}
translators.put(m, emptyTrue);
}
// object
translators.put(method(JavaLangObject,"<init>","()V"), emptyTrue);
translators.put(method(JavaLangObject,"equals","(Ljava/lang/Object;)Z"), javaLangObjectEquals());
translators.put(method(JavaLangObject,"hashCode","()I"), javaLangObjectHashCode());
// throwable
translators.put(method(JavaLangThrowable,"printStackTrace","()V"), emptyTrue);
translators.put(method(JavaLangThrowable,"fillInStackTrace","()Ljava/lang/Throwable;"), javaLangThrowableFillInStackTrace());
// system
translators.put(method(JavaLangSystem,"exit","(I)V"), emptyFalse);
// string
translators.put(method(JavaLangString,"equals","(Ljava/lang/Object;)Z"), javaLangStringEquals());
translators.put(method(JavaLangString,"length","()I"), javaLangStringLength());
translators.put(method(JavaLangString,"charAt","(I)C"), javaLangStringCharAt());
translators.put(method(JavaLangString,"hashCode","()I"), javaLangStringHashCode());
//nondet
final TypeName nonDetName = TypeName.string2TypeName("Ldata/angelic/NonDetChoice");
final TypeReference nonDet = TypeReference.findOrCreate(JavaSourceAnalysisScope.SOURCE, nonDetName);
translators.put(method(nonDet,"chooseObject","()Ljava/lang/Object;"), nonDetChoiceTranslator());
translators.put(method(nonDet,"chooseInt","()I"), nonDetChoiceTranslator());
translators.put(method(nonDet,"chooseBoolean","()Z"), nonDetChoiceTranslator());
return translators;
}
/** @return method reference corresponding to the given arguments */
private static MethodReference method(TypeReference type, String name, String descriptor) {
return MethodReference.findOrCreate(type,
Atom.findOrCreateUnicodeAtom(name),
Descriptor.findOrCreateUTF8(descriptor));
}
private static MethodTranslator nonDetChoiceTranslator(){
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
System.err.println("translating nondet!");
return result(TRUE, null, env);
}
};
}
/**
* @return a translator that simply propagates the initial heap through and
* pops the top frame with the given guard (return and exception values
* are set to null.)
*/
private static MethodTranslator emptyTranslator(final Formula exitGuard) {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
return result(exitGuard, null, env);
}
};
}
/** @return a spec translator for java.lang.Object.equals */
private static MethodTranslator javaLangObjectEquals() {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
return result(TRUE, env.refUse(1).eq(env.refUse(2)), env);
}
};
}
/** @return a spec translator for java.lang.Object.hashCode */
private static MethodTranslator javaLangObjectHashCode() {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
return result(TRUE, env.factory().systemHashCode().read(env.refUse(1)), env);
}
};
}
/** @return a spec translator for java.lang.Throwable.fillInStackTrace */
private static MethodTranslator javaLangThrowableFillInStackTrace() {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
return result(TRUE, env.refUse(1), env);
}
};
}
/** @return a spec translator for java.lang.String.hashCode */
private static MethodTranslator javaLangStringHashCode() {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
final Expression self = env.refUse(1);
final ExpressionFactory factory = env.factory();
final PhiExpression<IntExpression> phi = factory.valuePhi(IRType.INTEGER);
for(Map.Entry<FieldExpression<Expression>,Set<ArrayExpression<IntExpression>>>
fieldPiece : stringContents(factory).entrySet()) {
FieldExpression<Expression> fieldExpr = fieldPiece.getKey();
Formula fieldGuard = self.in(fieldExpr.instances());
Expression selfArrayRef = fieldExpr.read(self);
for(ArrayExpression<IntExpression> arrayExpr : fieldPiece.getValue()) {
Formula arrayGuard = selfArrayRef.in(arrayExpr.instances());
List<IntExpression> chars = new ArrayList<IntExpression>();
for(int i = 0, max = arrayExpr.cardinality(); i < max; i++) {
chars.add(arrayExpr.value(selfArrayRef, i));
}
phi.add(fieldGuard.and(arrayGuard), IntExpression.plus(chars));
}
}
return result(Formula.TRUE, phi.value(), env);
}
};
}
/** @return a spec translator for java.lang.String.charAt */
private static MethodTranslator javaLangStringCharAt() {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
final Expression self = env.refUse(1);
final IntExpression idx = env.intUse(2);
final ExpressionFactory factory = env.factory();
final PhiExpression<IntExpression> phi = factory.valuePhi(IRType.INTEGER);
for(Map.Entry<FieldExpression<Expression>,Set<ArrayExpression<IntExpression>>>
fieldPiece : stringContents(factory).entrySet()) {
FieldExpression<Expression> fieldExpr = fieldPiece.getKey();
Formula fieldGuard = self.in(fieldExpr.instances());
Expression selfArrayRef = fieldExpr.read(self);
for(ArrayExpression<IntExpression> arrayExpr : fieldPiece.getValue()) {
Formula arrayGuard = selfArrayRef.in(arrayExpr.instances());
phi.add(fieldGuard.and(arrayGuard), arrayExpr.read(selfArrayRef, idx));
}
}
return result(Formula.TRUE, phi.value(), env);
}
};
}
/** @return a spec translator for java.lang.String.length */
private static MethodTranslator javaLangStringLength() {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
final Expression self = env.refUse(1);
final ExpressionFactory factory = env.factory();
final PhiExpression<IntExpression> phi = factory.valuePhi(IRType.INTEGER);
for(Map.Entry<FieldExpression<Expression>,Set<FieldExpression<IntExpression>>>
lengthPiece : stringLengths(factory).entrySet()) {
FieldExpression<Expression> fieldExpr = lengthPiece.getKey();
Formula fieldGuard = self.in(fieldExpr.instances());
Expression selfArrayRef = fieldExpr.read(self);
for(FieldExpression<IntExpression> lengthExpr : lengthPiece.getValue()) {
Formula lengthGuard = selfArrayRef.in(lengthExpr.instances());
phi.add(fieldGuard.and(lengthGuard), lengthExpr.read(selfArrayRef));
}
}
return result(Formula.TRUE, phi.value(), env);
}
};
}
/** @return a spec translator for java.lang.String.equals */
private static MethodTranslator javaLangStringEquals() {
return new MethodTranslator() {
public MethodTranslation translate(Formula entryGuard, Environment env, MemoryInstructionHandler memoryHandler) {
final Expression self = env.refUse(1);
final Expression other = env.refUse(2);
final List<Formula> eqConditions = new ArrayList<Formula>();
eqConditions.add(self.eq(other));
final ExpressionFactory factory = env.factory();
for(Map.Entry<FieldExpression<Expression>,Set<ArrayExpression<IntExpression>>>
fieldPiece : stringContents(factory).entrySet()) {
FieldExpression<Expression> fieldExpr = fieldPiece.getKey();
Formula fieldGuard = self.in(fieldExpr.instances()).
and(other.in(fieldExpr.instances()));
Expression selfArrayRef = fieldExpr.read(self);
Expression otherArrayRef = fieldExpr.read(other);
for(ArrayExpression<IntExpression> arrayExpr : fieldPiece.getValue()) {
Formula arrayGuard = selfArrayRef.in(arrayExpr.instances()).
and(otherArrayRef.in(arrayExpr.instances()));
List<Formula> eqArray = new ArrayList<Formula>(arrayExpr.cardinality()+1);
eqArray.add(fieldGuard.and(arrayGuard));
for(int i = 0, max = arrayExpr.cardinality(); i < max; i++) {
Formula sameIdx = arrayExpr.index(selfArrayRef, i).
eq(arrayExpr.index(otherArrayRef, i));
Formula sameChar = arrayExpr.value(selfArrayRef, i).
eq(arrayExpr.value(otherArrayRef, i));
eqArray.add(sameIdx.and(sameChar));
}
eqConditions.add(Formula.and(eqArray));
}
}
return result(Formula.TRUE, Formula.or(eqConditions), env);
}
};
}
/**
* Returns a Result from the given values and env.top.
* (The method is assumed to throw no exception, so exception is set to null)
* The topmost frame in the environment is updated before
* being placed into the final result by propagating initial
* heap values into unfilled exit values, if any.
* @return a a Result from the given values and env.top
*/
@SuppressWarnings("unchecked")
private static MethodTranslation result(final Formula exitGuard, final Object returnValue, final Environment env) {
final FieldSSATable fieldSSA = env.top().callInfo().fieldSSA();
for(Iterator<PointerKey> fields = fieldSSA.getFields(); fields.hasNext();) {
PointerKey field = fields.next();
int entry = fieldSSA.getEntryValue(field);
int exit = fieldSSA.getExitValue(field);
if (env.heapUse(exit)==null)
env.heapDef(exit, env.heapUse(entry));
}
return new MethodTranslation() {
final Frame frame = env.pop();
public Set<Formula> assertions() { return Collections.EMPTY_SET; }
public Set<TranslationWarning> warnings() { return Collections.EMPTY_SET; }
public Set<Formula> assumptions() { return Collections.EMPTY_SET;}
public <T> T returnValue() { return (T) returnValue; }
public Expression exceptionValue() { return null; }
public Formula normalExitGuard() { return exitGuard; }
public Frame frame() { return frame; }
};
}
/** @return the field object representing the value field of the String class. */
private static IField stringValueField(WalaInformation info) {
final IClass str = info.callGraph().getClassHierarchy().lookupClass(TypeReference.JavaLangString);
return str.getField(Atom.findOrCreateUnicodeAtom("value"));
}
/**
* @return a map from each FieldExpression F corresponding to
* String.value field in the given factory.info.callGraph(),
* to the set of array expressions that represent the contents of the
* array instances pointed to by F.
**/
private static Map<FieldExpression<Expression>,Set<ArrayExpression<IntExpression>>> stringContents(ExpressionFactory factory) {
final IField field = stringValueField(factory.info());
final Map<InstanceKey, FieldExpression<Expression>> tmp =
new LinkedHashMap<InstanceKey, FieldExpression<Expression>>();
final Map<FieldExpression<Expression>,Set<ArrayExpression<IntExpression>>> ret =
new LinkedHashMap<FieldExpression<Expression>, Set<ArrayExpression<IntExpression>>>();
final WalaInformation info = factory.info();
for(PointerKey key : info.relevantFields()) {
if (key instanceof InstanceFieldKey &&
((InstanceFieldKey)key).getField().equals(field)) {
FieldExpression<Expression> fieldExpr = factory.initValueOf(key);
ret.put(fieldExpr, new LinkedHashSet<ArrayExpression<IntExpression>>());
for(InstanceKey pointedTo : info.pointsTo(key)) {
tmp.put(pointedTo, fieldExpr);
}
}
}
for(PointerKey key : info.relevantFields()) {
if (key instanceof ArrayContentsKey && tmp.containsKey(((ArrayContentsKey)key).getInstanceKey())) {
ArrayExpression<IntExpression> arrayExpr = factory.initValueOf(key);
ret.get(tmp.get(((ArrayContentsKey)key).getInstanceKey())).add(arrayExpr);
}
}
return ret;
}
/**
* @return a map from each FieldExpression F corresponding to
* String.value field in the given factory.info.callGraph(),
* to the set of field expressions that represent the length of the
* array instances pointed to by F.
**/
private static Map<FieldExpression<Expression>,Set<FieldExpression<IntExpression>>> stringLengths(ExpressionFactory factory) {
final IField field = stringValueField(factory.info());
final Map<InstanceKey, FieldExpression<Expression>> tmp =
new LinkedHashMap<InstanceKey, FieldExpression<Expression>>();
final Map<FieldExpression<Expression>,Set<FieldExpression<IntExpression>>> ret =
new LinkedHashMap<FieldExpression<Expression>, Set<FieldExpression<IntExpression>>>();
final WalaInformation info = factory.info();
for(PointerKey key : info.relevantFields()) {
if (key instanceof InstanceFieldKey &&
((InstanceFieldKey)key).getField().equals(field)) {
FieldExpression<Expression> fieldExpr = factory.initValueOf(key);
ret.put(fieldExpr, new LinkedHashSet<FieldExpression<IntExpression>>());
for(InstanceKey pointedTo : info.pointsTo(key)) {
tmp.put(pointedTo, fieldExpr);
}
}
}
for(PointerKey key : info.relevantFields()) {
if (key instanceof ArrayLengthKey && tmp.containsKey(((ArrayContentsKey)key).getInstanceKey())) {
FieldExpression<IntExpression> lengthExpr = factory.initValueOf(key);
ret.get(((ArrayContentsKey)key).getInstanceKey()).add(lengthExpr);
}
}
return ret;
}
}