package gov.nasa.jpf.util.script; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import gov.nasa.jpf.Config; import gov.nasa.jpf.ListenerAdapter; import gov.nasa.jpf.search.Search; import gov.nasa.jpf.util.DynamicObjectArray; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.SystemState; /** * abstract root for backtrackable event generator factories * * <2do> - we don't support backtracking for sections yet! needs to be implemented for * state charts */ @SuppressWarnings("rawtypes") public abstract class EventGeneratorFactory extends ListenerAdapter implements ElementProcessor, Iterable<EventGenerator> { static final String DEFAULT = "default"; // helper class to store our internal state. For a simple script based system, // storing the 'cur' index (into the queue) would do, but the queue might have been // generated dynamically, so we need some container to store both static class Memento { ArrayList<EventGenerator> queue; int cur; // cursor into queue Memento (EventGeneratorFactory fact) { queue = fact.queue; cur = fact.cur; } void restore (EventGeneratorFactory fact) { fact.queue = queue; fact.cur = cur; } } // <2do> this is lame - if we really want 'instructions' in the queue, rather // than data (== CGs), then we should have a queue of general EventOp entries // this is only used for unbounded REPEATs so far // <2do> the nextCG is currently unconditionally reset in getNextChoiceGenerator() // so we have to make sure we don't jump back if the jump target state was already // visited, which we have to store in the Jump static class Loop extends EventGenerator { int startPos, endPos; Loop (String id, int startPos, int endPos){ super(id); this.startPos = startPos; this.endPos = endPos; } int getStartPos() { return startPos; } //--- those are all dummies - this isn't really a choice public void advance() {} public Class<?> getChoiceType() { return null; } public Object getNextChoice() { return null; } public int getProcessedNumberOfChoices() { return 0; } public int getTotalNumberOfChoices() { return 0; } public boolean hasMoreChoices() { return false; } public ChoiceGenerator<?> randomize() { return null; } public void reset() {} } /** the last returned position in the generator stream */ protected int cur; /** this is where we store the cur positions for backtracking and restoring states */ DynamicObjectArray<Memento> states; protected String scriptFileName; protected Script script; protected Config conf; protected LinkedHashMap<String,ArrayList<EventGenerator>> sections; protected ArrayList<EventGenerator> queue; EventFactory efact; protected EventGeneratorFactory () { efact = null; } protected EventGeneratorFactory (EventFactory efact) { this.efact = efact; } protected void init (String fname) throws ESParser.Exception { cur = 0; states = new DynamicObjectArray<Memento>(); sections = new LinkedHashMap<String,ArrayList<EventGenerator>>(); queue = new ArrayList<EventGenerator>(); sections.put(DEFAULT, queue); ESParser parser= new ESParser(fname, efact); script = parser.parse(); scriptFileName = fname; script.process(this); } public Iterator<EventGenerator> iterator() { return queue.iterator(); } protected void addLoop (int startPos) { queue.add( new Loop( "loop", startPos, queue.size()-1)); } public abstract Class<?> getEventType(); /** * reset the enumeration state of this factory */ public void reset () { cur = 0; } public String getScriptFileName() { return scriptFileName; } public Script getScript() { return script; } public boolean hasSection (String id) { return sections.containsKey(id); } public ArrayList<EventGenerator> getSection (String id) { return sections.get(id); } public ArrayList<EventGenerator> getDefaultSection () { return sections.get(DEFAULT); } protected void setQueue (ArrayList<EventGenerator> q) { if (queue != q) { queue = q; cur = 0; } } protected EventGenerator getNextEventGenerator() { EventGenerator cg; int n = queue.size(); if (n == 0) { return null; // nothing to do } if (cur < n) { cg = getQueueItem(cur); // might clone the queue item // <2do> - this is a BAD hot fix, but it's going away soon! if (cg instanceof Loop) { int tgtPos = ((Loop)cg).getStartPos(); cg = queue.get(tgtPos); if (!cg.hasMoreChoices()) { for (int i=tgtPos; i<cur; i++) { queue.get(i).reset(); } } cur = tgtPos; } cg.setId(Integer.toString(cur)); // might be reused if we re-enter a section sequence or REPEAT body, so we have to reset // <2do> - commenting this out leads to premature state matching on model loops // (will be fixed by the revamped environment modeling) //cg.reset(); cur++; return cg; } else { return null; // nothing left } } // we encapsulate this because it might require cloning protected EventGenerator getQueueItem (int i) { return queue.get(i); } public int getTotalNumberOfEvents() { int total=0; int last = 1; for (EventGenerator cg : queue) { int level = cg.getTotalNumberOfChoices() * last; total += level; last = level; } return total; } public void printOn (PrintWriter pw) { for (EventGenerator eg : queue) { pw.println(eg); } } /************************************** SearchListener interface **************/ /* we need this after a backtrack and restore to determine the next CG to return */ @Override public void searchStarted (Search search) { cur = 0; } @Override public void stateAdvanced (Search search) { int idx = search.getStateId(); if (idx >= 0) { // <??> why would it be notified for the init state? Memento m = new Memento(this); states.set(idx, m); } } @Override public void stateBacktracked (Search search) { Memento m = states.get(search.getStateId()); m.restore(this); // nextCg will be re-computed (->getNext), so there is no need to reset } @Override public void stateRestored (Search search) { Memento m = states.get(search.getStateId()); m.restore(this); // nextCg is restored (not re-computed), so we need to reset SystemState ss = search.getVM().getSystemState(); ChoiceGenerator<?> cgNext = ss.getNextChoiceGenerator(); cgNext.reset(); } }