/******************************************************************************
* 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.representation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import com.ibm.wala.memsat.util.Nodes;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.Relation;
import kodkod.engine.Evaluator;
/**
* Wraps a Kodkod expression that represents the contents of a given field.
*
*
* @specfield isStatic: boolean // true if the field is static
* @specfield refs: lone Expression // if non-static, expression representing the objects whose field value is represented by this field expression
* @specfield field: Expression // expression representing the contents of the field
* @author Emina Torlak
*/
public final class FieldExpression<T> extends HeapExpression<T> {
private final Expression field;
private final Expression domain;
private final Interpreter<T> meaning;
/**
* Constructs a new field from the given name, domain, and meaning function.
* @effects this.refs' = domain and (this.isStatic' iff domain = null) and
* this.field' = Relation.nary(name, meaning.zero.arity + (isStatic => 0 else 1))
*/
private FieldExpression(String name, Expression domain, Interpreter<T> meaning) {
this.field = Relation.nary(name, meaning.defaultObj().arity() + (domain==null ? 0 : 1));
this.domain = domain;
this.meaning = meaning;
}
/**
* Constructs a new field using the given template and new field value.
* @effects this.isStatic' = old.isStatic and
* this.field' = field) }
*/
private FieldExpression(FieldExpression<T> old, Expression field) {
this.domain = old.domain;
this.meaning = old.meaning;
this.field = field;
}
/**
* Returns a field constructed from the given name, domain, and meaning function.
* @effects { f: FieldExpression<T> | f.refs' = refs and
* f.isStatic iff refs = null and
* f.field = Relation.nary(name, valM.zero.arity + (isStatic => 0 else 1)) }
*/
static <T> FieldExpression<T> field(String name, Expression refs, Interpreter<T> valM) {
return new FieldExpression<T>(name,refs,valM);
}
public String toString() {
return field.toString();
}
/**
* {@inheritDoc}
* @see com.ibm.wala.memsat.representation.HeapExpression#valueInterpreter()
*/
public final Interpreter<T> valueInterpreter() { return meaning; }
/**
* Returns this field.
* @return this.field
*/
final Expression field() { return field; }
/**
* Returns false.
* @return false
* @see com.ibm.wala.memsat.representation.HeapExpression#isArray()
*/
public final boolean isArray() { return false; }
/**
* Returns the Expression encoding the set of instances whose field is
* represented by this field expression.
* @return this.refs
*/
public final Expression instances() { return domain; }
/**
* Returns true if this represents a static field.
* @return this.isStatic
*/
public final boolean isStatic() { return domain==null; }
/**
* Returns the value of this field at the given location.
* If this field is static, the reference argument is ignored
* (it can be null).
* @requires ref.arity = 1
* @return { t: T | [[n]]= (this.isStatic => this.field else ref.join(this.field)) }
*/
public final T read(Expression ref) {
return meaning.fromObj(isStatic() ? field : ref.join(field));
}
/**
* Returns a new field expression that reflects the state
* of the this field after the given value has been written
* to the given location.
* If this field is static, the reference argument is ignored
* (it can be null).
* @requires ref.arity = 1
* @return { f: FieldExpression<N> |
* f.isStatic = this.isStatic and
* f.field = this.field++(this.isStatic => [[value]] else ref->[[value]]) }
*/
public final FieldExpression<T> write(Expression ref, T value) {
final Expression expr = meaning.toObj(value);
final Expression overrider = isStatic() ? expr : ref.product(expr);
return new FieldExpression<T>(this, isStatic() ? overrider :
field.difference(ref.product(ref.join(field))).union(overrider));
}
/**
* Returns the value of this field expression at the specified reference, as given by the
* specified evaluator. The reference expression is ignored (it can be null) if
* this is a static field. The runtime type of the returned object is
* determined by the type of values stored in this field. In particular, if
* this field stores RealExpressions, then the returned value will be
* a Float; if this field stores IntExpressions, the returned value will be an Integer;
* if this field stores Expressions, the returned value will be an Object;
* finally, if this field stores Formulas, the returned value will be a Boolean.
* @requires the given evaluator's instance has bindings
* for all relations (if any) at the leaves of this field expression.
* @requires eval.evaluate(ref) in eval.evaluate(this.refs)
* @return value of this field expression at the specified reference, as given by the
* specified evaluator.
*/
@SuppressWarnings("unchecked")
public final <V> V evaluate(Expression ref, Evaluator eval) {
final T value = isStatic() ? meaning.fromObj(field) : meaning.fromObj(ref.join(field));
return (V) meaning.evaluate(value, eval);
}
/**
* Returns a field expression that evaluates to the ith value returned by the
* fields' iterator iff the ith guard returned by the guards' iterator evaluates
* to true. This method assumes that at most one of the given guards
* evaluates to true. If none do, the returned value will evaluate to
* an empty field (i.e. a field that maps everything in its domain to
* a T whose Expression value is the empty set).
* @requires guards and values contain the same (positive) number of objects
* @requires at most one of the guards can ever evaluate to true.
* @requires all field expressions in the given collection represent the same field
* @return a field expression that evaluates to the ith value returned by the
* fields' iterator iff the ith guard returned by the guards' iterator evaluates
* to true.
*/
static <T> FieldExpression<T> phi(Collection<Formula> guards, Collection<FieldExpression<T>> fields) {
assert guards.size() == fields.size();
assert !guards.isEmpty();
final List<Expression> phis = new ArrayList<Expression>(guards.size());
final Iterator<Formula> gItr = guards.iterator();
final Iterator<FieldExpression<T>> fItr = fields.iterator();
final FieldExpression<T> first = fItr.next();
final Expression empty = Nodes.empty(first.field.arity());
phis.add(gItr.next().thenElse(first.field, empty));
while(gItr.hasNext()) {
phis.add(gItr.next().thenElse(fItr.next().field, empty));
}
return new FieldExpression<T>(first, phis.isEmpty() ? empty : Expression.union(phis));
}
}