Mercurial > hg > Members > kono > jpf-core
diff src/main/gov/nasa/jpf/util/script/ScriptEnvironment.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/gov/nasa/jpf/util/script/ScriptEnvironment.java Fri Jan 23 10:14:01 2015 -0800 @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2014, United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The Java Pathfinder core (jpf-core) platform is licensed under the + * Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package gov.nasa.jpf.util.script; + +import gov.nasa.jpf.JPF; +import gov.nasa.jpf.util.StateExtensionClient; +import gov.nasa.jpf.util.StateExtensionListener; +import gov.nasa.jpf.vm.ChoiceGenerator; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; +import java.util.List; + +/** + * class representing a statemachine environment that produces SCEventGenerators + * from scripts + */ +public abstract class ScriptEnvironment<CG extends ChoiceGenerator<?>> + implements StateExtensionClient<ScriptEnvironment<CG>.ActiveSnapshot> { + + static final String DEFAULT = "default"; + + //--- just a helper tuple + static class ActiveSequence implements Cloneable { + String stateName; + Section section; + SequenceInterpreter intrp; + + public ActiveSequence (String stateName, Section section, SequenceInterpreter intrp) { + this.stateName = stateName; + this.section = section; + this.intrp = intrp; + } + + @Override + public Object clone() { + try { + ActiveSequence as = (ActiveSequence) super.clone(); + as.intrp = (SequenceInterpreter) intrp.clone(); + return as; + } catch (CloneNotSupportedException nonsense) { + return null; // we are a Cloneable, so we don't get here + } + } + + public boolean isDone() { + return intrp.isDone(); + } + } + + //--- our state extension - we need this mostly for cloning (deep copy) + class ActiveSnapshot implements Cloneable { + ActiveSequence[] actives; + + ActiveSnapshot () { + actives = new ActiveSequence[0]; + } + + ActiveSnapshot (ActiveSequence[] as) { + actives = as; + } + + public ActiveSequence get (String stateName) { + for (ActiveSequence as : actives) { + if (as.stateName.equals(stateName)) { + return as; + } + } + return null; + } + + @Override + public Object clone() { + try { + ActiveSnapshot ss = (ActiveSnapshot)super.clone(); + for (int i=0; i<actives.length; i++) { + ActiveSequence as = actives[i]; + ss.actives[i] = (ActiveSequence)as.clone(); + } + return ss; + } catch (CloneNotSupportedException nonsense) { + return null; // we are a Cloneable, so we don't get here + } + } + + ActiveSnapshot advance (String[] activeStates, BitSet isReEntered) { + ActiveSequence[] newActives = new ActiveSequence[activeStates.length]; + + //--- carry over the persisting entries + for (int i=0; i<activeStates.length; i++) { + String sn = activeStates[i]; + for (ActiveSequence as : actives) { + if (as.stateName.equals(sn) ) { + // we could use isReEntered to determine if we want to restart sequences + // <2do> how do we factor this out as policy? + newActives[i] = (ActiveSequence)as.clone(); + } + } + } + + //--- add the new ones + int skipped = 0; + nextActive: + for (int i=0; i<activeStates.length; i++) { + if (newActives[i] == null) { + // get the script section + Section sec = getSection(activeStates[i]); + if (sec != null) { + + // check if that section is already processed by another active state, in which case we skip + for (int j=0; j<newActives.length; j++) { + if (newActives[j] != null && newActives[j].section == sec) { + skipped++; + continue nextActive; + } + } + + // check if it was processed by a prev state (superstate section by a + // common parent of a new and an old state - this is the common case + for (int j=0; j<actives.length; j++) { + // <2do> how do we handle state re-entering? + if (actives[j].section == sec) { + ActiveSequence as = new ActiveSequence(activeStates[i], sec, actives[j].intrp); + newActives[i] = as; + continue nextActive; + } + } + + // it's a new one + ActiveSequence as = new ActiveSequence(activeStates[i], sec, + new SequenceInterpreter(sec)); + newActives[i] = as; + + } else { // sec == null : we didn't find any sequence for this state + skipped++; + } + } + } + + //--- compress if we skipped any active states + if (skipped > 0) { + int n = activeStates.length - skipped; + ActiveSequence[] na = new ActiveSequence[n]; + for (int i=0, j=0; j<n; i++) { + if (newActives[i] != null) { + na[j++] = newActives[i]; + } + } + newActives = na; + } + + return new ActiveSnapshot(newActives); + } + } + + //--- start of ScriptEnvronment + + String scriptName; + Reader scriptReader; + Script script; + ActiveSnapshot cur; + + HashMap<String,Section> sections = new HashMap<String,Section>(); + Section defaultSection; + + //--- initialization + public ScriptEnvironment (String fname) throws FileNotFoundException { + this( fname, new FileReader(fname)); + } + + public ScriptEnvironment (String name, Reader r) { + this.scriptName = name; + this.scriptReader = r; + } + + public void parseScript () throws ESParser.Exception { + ESParser parser= new ESParser(scriptName, scriptReader); + script = parser.parse(); + + initSections(); + + cur = new ActiveSnapshot(); + } + + void initSections() { + Section defSec = new Section(script, DEFAULT); + + for (ScriptElement e : script) { + + if (e instanceof Section) { + Section sec = (Section)e; + List<String> secIds = sec.getIds(); + if (secIds.size() > 0) { + for (String id : secIds) { + sections.put(id, (Section)sec.clone()); // clone to guarantee different identities + } + } else { + sections.put(secIds.get(0), sec); + } + } else { // add copy to default sequence + defSec.add(e.clone()); + } + } + + if (defSec.getNumberOfChildren() > 0) { + defaultSection = defSec; + } + } + + Section getSection (String id) { + Section sec = null; + + while (id != null) { + sec = sections.get(id); + if (sec != null) { + return sec; + } + + int idx = id.lastIndexOf('.'); + if (idx > 0) { + id = id.substring(0, idx); // ?? do we really want this recursive? that's policy + } else { + id = null; + } + } + + return defaultSection; + } + + void addExpandedEvent(ArrayList<Event> events, Event se) { + for (Event e : se.expand()) { + if (!events.contains(e)) { + events.add(e); + } + } + } + + static final String[] ACTIVE_DEFAULT = { DEFAULT }; + + public CG getNext (String id) { + return getNext(id, ACTIVE_DEFAULT, null); + } + + public CG getNext (String id, String[] activeStates) { + return getNext(id, activeStates, null); + } + + // this is our main purpose in life, but there is some policy in here + public CG getNext (String id, String[] activeStates, BitSet isReEntered) { + + cur = cur.advance(activeStates, isReEntered); + + ArrayList<Event> events = new ArrayList<Event>(cur.actives.length); + for (ActiveSequence as : cur.actives) { + + while (true) { + ScriptElement se = as.intrp.getNext(); + if (se != null) { + if (se instanceof Event) { + addExpandedEvent(events, (Event)se); + break; + } else if (se instanceof Alternative) { + for (ScriptElement ase : (Alternative)se) { + if (ase instanceof Event) { + addExpandedEvent(events, (Event)ase); + } + } + break; + } else { + // get next event + } + } else { + break; // process next active sequence + } + } + } + + return createCGFromEvents(id, events); + } + + protected abstract CG createCGFromEvents(String id, List<Event> events); + + //--- StateExtension interface + @Override + public ActiveSnapshot getStateExtension() { + return cur; + } + + @Override + public void restore(ActiveSnapshot stateExtension) { + cur = stateExtension; + } + + @Override + public void registerListener(JPF jpf) { + StateExtensionListener<ActiveSnapshot> sel = new StateExtensionListener(this); + jpf.addSearchListener(sel); + } + +}