//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.vm;
import java.lang.reflect.Array;
import java.util.Comparator;
import java.util.Random;
import cmu.conditional.Conditional;
import java.util.function.Function;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.util.ObjectList;
/**
* abstract root class for configurable choice generators
*/
@SuppressWarnings({"hiding","unchecked"})
public abstract class ChoiceGeneratorBase<T> implements ChoiceGenerator<T> {
/**
* choice randomization policies, which can be set from JPF configuration
*/
static enum ChoiceRandomizationPolicy {
VAR_SEED, // randomize choices using a different seed for every JPF run
FIXED_SEED, // randomize choices using a fixed seed for each JPF run (reproducible, seed can be specified as cg.seed)
NONE // don't randomize choices
};
static ChoiceRandomizationPolicy randomization;
// the marker for the current choice used in String conversion
public static final char MARKER = '>';
protected static Random random = new Random(42);
// want the id to be visible to subclasses outside package
protected String id;
// for subsequent access, there is no need to translate a JPF String object reference
// into a host VM String anymore (we just need it for creation to look up
// the class if this is a named CG)
protected int idRef;
// used to cut off further choice enumeration
protected boolean isDone;
// we keep a linked list of CG's
protected ChoiceGenerator<?> prev;
// the instruction that created this CG
protected Conditional<Instruction> insn;
// and the thread that executed this insn
protected ThreadInfo ti;
// free attributes (set on demand)
protected Object attr;
// answer if this is a cascaded CG, i.e. we had more than one registered
// within the same transition. Note this is NOT set for the last CG registered
protected boolean isCascaded;
// in case this is initialized from a VM context
public static void init(Config config) {
randomization = config.getEnum("cg.randomize_choices",
ChoiceRandomizationPolicy.values(), ChoiceRandomizationPolicy.NONE);
// if the randomize_choices is set to random then we need to
// pick the seed based on the system time.
if (randomization == ChoiceRandomizationPolicy.VAR_SEED) {
random.setSeed(System.currentTimeMillis());
} else if (randomization == ChoiceRandomizationPolicy.FIXED_SEED){
long seed = config.getLong("cg.seed", 42);
random.setSeed( seed);
}
}
public static boolean useRandomization() {
return (randomization != ChoiceRandomizationPolicy.NONE);
}
/**
* don't use this since it is not safe for cascaded ChoiceGenerators
* (we need the 'id' to be as context specific as possible)
*/
@Deprecated
protected ChoiceGeneratorBase() {
id = "?";
}
protected ChoiceGeneratorBase(String id) {
this.id = id;
}
public ChoiceGeneratorBase<?> clone() throws CloneNotSupportedException {
return (ChoiceGeneratorBase<?>)super.clone();
}
public ChoiceGenerator<?> deepClone() throws CloneNotSupportedException {
ChoiceGenerator<?> clone = (ChoiceGenerator<?>) super.clone();
// we need to deep copy the parent CG
if (prev != null){
clone.setPreviousChoiceGenerator( prev.deepClone());
}
return clone;
}
public String getId() {
return id;
}
public int getIdRef() {
return idRef;
}
public void setIdRef(int idRef) {
this.idRef = idRef;
}
public void setId(String id) {
this.id = id;
}
public boolean isSchedulingPoint() {
return false;
}
//--- the getters and setters for the CG creation info
public void setThreadInfo(ThreadInfo ti) {
this.ti = ti;
}
public ThreadInfo getThreadInfo() {
return ti;
}
public void setInsn(Conditional<Instruction> insn) {
this.insn = insn;
}
public Conditional<Instruction> getInsn() {
return insn;
}
public void setContext(ThreadInfo tiCreator) {
ti = tiCreator;
insn = tiCreator.getPC();
}
public Conditional<String> getSourceLocation() {
// return insn.getSourceLocation();
return insn.map(new Function<Instruction, String>() {
@Override
public String apply(Instruction x) {
return x.getSourceLocation();
}
}).simplify();
}
public boolean supportsReordering(){
return false;
}
/**
* reorder according to a user provided comparator
* @returns instance to reordered CG of same choice type,
* null if not supported by particular CG subclass
*
* Note: this should only be called before the first advance, since it
* can reset the CG enumeration status
*/
public ChoiceGenerator<T> reorder (Comparator<T> comparator){
return null;
}
public void setPreviousChoiceGenerator(ChoiceGenerator<?> cg) {
prev = cg;
}
public void setCascaded() {
isCascaded = true;
}
public boolean isCascaded() {
return isCascaded;
}
public <C extends ChoiceGenerator<?>> C getPreviousChoiceGeneratorOfType(Class<C> cls) {
ChoiceGenerator<?> cg = prev;
while (cg != null) {
if (cls.isInstance(cg)) {
return (C) cg;
}
cg = cg.getPreviousChoiceGenerator();
}
return null;
}
/**
* returns the prev CG if it was registered for the same insn
*/
public ChoiceGenerator<?> getCascadedParent() {
if (prev != null) {
if (prev.isCascaded()) {
return prev;
}
}
return null;
}
/**
* return array with all cascaded parents and this CG, in registration order
*/
public ChoiceGenerator<?>[] getCascade() {
int n = 0;
for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getCascadedParent()) {
n++;
}
ChoiceGenerator<?>[] a = new ChoiceGenerator<?>[n];
for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getCascadedParent()) {
a[--n] = cg;
}
return a;
}
/**
* return array with all parents and this CG, in registration order
*/
public ChoiceGenerator<?>[] getAll() {
int n = 0;
for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
n++;
}
ChoiceGenerator<?>[] a = new ChoiceGenerator<?>[n];
for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
a[--n] = cg;
}
return a;
}
/**
* return array with all CGs (including this one) of given 'cgType', in registration order
*/
public <C extends ChoiceGenerator<?>> C[] getAllOfType(Class<C> cgType) {
int n = 0;
for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
if (cgType.isAssignableFrom(cg.getClass())) {
n++;
}
}
C[] a = (C[]) Array.newInstance(cgType, n);
for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
if (cgType.isAssignableFrom(cg.getClass())) {
a[--n] = (C) cg;
}
}
return a;
}
// we can't put the advanceForCurrentInsn() here because it has to do
// notifications, which are the SystemState responsibility
/**
* advance n choices
* pretty braindead generic solution, but if more speed is needed, we can easily override
* in the concrete CGs (it's used for path replay)
*/
public void advance(int nChoices) {
while (nChoices-- > 0) {
advance();
}
}
public void select(int nChoice) {
advance(nChoice);
setDone();
}
public boolean isDone() {
return isDone;
}
public void setDone() {
isDone = true;
}
public boolean isProcessed() {
return isDone || !hasMoreChoices();
}
//--- the generic attribute API
public boolean hasAttr() {
return (attr != null);
}
public boolean hasAttr(Class<?> attrType) {
return ObjectList.containsType(attr, attrType);
}
public boolean hasAttrValue (Object a){
return ObjectList.contains(attr, a);
}
/**
* this returns all of them - use either if you know there will be only
* one attribute at a time, or check/process result with ObjectList
*/
public Object getAttr() {
return attr;
}
/**
* this replaces all of them - use only if you know
* - there will be only one attribute at a time
* - you obtained the value you set by a previous getXAttr()
* - you constructed a multi value list with ObjectList.createList()
*/
public void setAttr(Object a) {
attr = a;
}
public void addAttr(Object a) {
attr = ObjectList.add(attr, a);
}
public void removeAttr(Object a) {
attr = ObjectList.remove(attr, a);
}
public void replaceAttr(Object oldAttr, Object newAttr) {
attr = ObjectList.replace(attr, oldAttr, newAttr);
}
/**
* this only returns the first attr of this type, there can be more
* if you don't use client private types or the provided type is too general
*/
public <T> T getAttr(Class<T> attrType) {
return ObjectList.getFirst(attr, attrType);
}
public <T> T getNextAttr(Class<T> attrType, Object prev) {
return ObjectList.getNext(attr, attrType, prev);
}
public ObjectList.Iterator attrIterator() {
return ObjectList.iterator(attr);
}
public <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType) {
return ObjectList.typedIterator(attr, attrType);
}
// -- end attrs --
public String toString() {
StringBuilder b = new StringBuilder(getClass().getName());
b.append(" {id:\"");
b.append(id);
b.append("\" ,");
b.append(getProcessedNumberOfChoices());
b.append('/');
b.append(getTotalNumberOfChoices());
b.append(",isCascaded:");
b.append(isCascaded);
if (attr != null) {
b.append(",attrs:[");
int i = 0;
for (Object a : ObjectList.iterator(attr)) {
if (i++ > 1) {
b.append(',');
}
b.append(a);
}
b.append(']');
}
b.append('}');
return b.toString();
}
public ChoiceGenerator<?> getPreviousChoiceGenerator() {
return prev;
}
// override if there is special choice randomization support
public ChoiceGenerator<T> randomize(){
return this;
}
}