package org.overture.interpreter.eval;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.definitions.AClassInvariantDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.expressions.PExp;
import org.overture.ast.intf.lex.ILexLocation;
import org.overture.ast.lex.Dialect;
import org.overture.ast.patterns.ASeqBind;
import org.overture.ast.patterns.ASetBind;
import org.overture.ast.patterns.ATypeBind;
import org.overture.ast.statements.AAlwaysStm;
import org.overture.ast.statements.AApplyObjectDesignator;
import org.overture.ast.statements.AAssignmentStm;
import org.overture.ast.statements.AAtomicStm;
import org.overture.ast.statements.ABlockSimpleBlockStm;
import org.overture.ast.statements.ACallObjectStm;
import org.overture.ast.statements.ACallStm;
import org.overture.ast.statements.ACaseAlternativeStm;
import org.overture.ast.statements.ACasesStm;
import org.overture.ast.statements.AClassInvariantStm;
import org.overture.ast.statements.ACyclesStm;
import org.overture.ast.statements.ADurationStm;
import org.overture.ast.statements.AElseIfStm;
import org.overture.ast.statements.AErrorStm;
import org.overture.ast.statements.AExitStm;
import org.overture.ast.statements.AFieldObjectDesignator;
import org.overture.ast.statements.AFieldStateDesignator;
import org.overture.ast.statements.AForAllStm;
import org.overture.ast.statements.AForIndexStm;
import org.overture.ast.statements.AForPatternBindStm;
import org.overture.ast.statements.AIdentifierObjectDesignator;
import org.overture.ast.statements.AIdentifierStateDesignator;
import org.overture.ast.statements.AIfStm;
import org.overture.ast.statements.ALetBeStStm;
import org.overture.ast.statements.ALetStm;
import org.overture.ast.statements.AMapSeqStateDesignator;
import org.overture.ast.statements.ANewObjectDesignator;
import org.overture.ast.statements.ANonDeterministicSimpleBlockStm;
import org.overture.ast.statements.ANotYetSpecifiedStm;
import org.overture.ast.statements.APeriodicStm;
import org.overture.ast.statements.AReturnStm;
import org.overture.ast.statements.ASelfObjectDesignator;
import org.overture.ast.statements.ASkipStm;
import org.overture.ast.statements.ASpecificationStm;
import org.overture.ast.statements.ASporadicStm;
import org.overture.ast.statements.AStartStm;
import org.overture.ast.statements.AStopStm;
import org.overture.ast.statements.ASubclassResponsibilityStm;
import org.overture.ast.statements.ATixeStm;
import org.overture.ast.statements.ATixeStmtAlternative;
import org.overture.ast.statements.ATrapStm;
import org.overture.ast.statements.AWhileStm;
import org.overture.ast.statements.PStm;
import org.overture.ast.statements.SSimpleBlockStm;
import org.overture.ast.types.AUnionType;
import org.overture.ast.types.PType;
import org.overture.config.Release;
import org.overture.config.Settings;
import org.overture.interpreter.debug.BreakpointManager;
import org.overture.interpreter.messages.rtlog.RTExtendedTextMessage;
import org.overture.interpreter.messages.rtlog.RTLogger;
import org.overture.interpreter.runtime.ClassInterpreter;
import org.overture.interpreter.runtime.Context;
import org.overture.interpreter.runtime.ContextException;
import org.overture.interpreter.runtime.ExitException;
import org.overture.interpreter.runtime.ObjectContext;
import org.overture.interpreter.runtime.PatternMatchException;
import org.overture.interpreter.runtime.RootContext;
import org.overture.interpreter.runtime.ValueException;
import org.overture.interpreter.runtime.VdmRuntime;
import org.overture.interpreter.runtime.VdmRuntimeError;
import org.overture.interpreter.scheduler.BasicSchedulableThread;
import org.overture.interpreter.scheduler.ISchedulableThread;
import org.overture.interpreter.scheduler.ObjectThread;
import org.overture.interpreter.scheduler.PeriodicThread;
import org.overture.interpreter.scheduler.SharedStateListner;
import org.overture.interpreter.values.BooleanValue;
import org.overture.interpreter.values.FunctionValue;
import org.overture.interpreter.values.IntegerValue;
import org.overture.interpreter.values.MapValue;
import org.overture.interpreter.values.ObjectValue;
import org.overture.interpreter.values.OperationValue;
import org.overture.interpreter.values.RecordValue;
import org.overture.interpreter.values.SeqValue;
import org.overture.interpreter.values.SetValue;
import org.overture.interpreter.values.UndefinedValue;
import org.overture.interpreter.values.UpdatableValue;
import org.overture.interpreter.values.Value;
import org.overture.interpreter.values.ValueList;
import org.overture.interpreter.values.ValueListenerList;
import org.overture.interpreter.values.ValueMap;
import org.overture.interpreter.values.ValueSet;
import org.overture.interpreter.values.VoidReturnValue;
import org.overture.interpreter.values.VoidValue;
import org.overture.parser.config.Properties;
public class StatementEvaluator extends DelegateExpressionEvaluator
{
@Override
public Value caseAAlwaysStm(AAlwaysStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
Value rv = null;
ExitException bodyRaised = null;
try
{
rv = node.getBody().apply(VdmRuntime.getStatementEvaluator(), ctxt);
} catch (ExitException e)
{
// Finally clause executes the "always" statement, but we
// re-throw this exception, unless the always clause raises one.
bodyRaised = e;
} finally
{
node.getAlways().apply(VdmRuntime.getStatementEvaluator(), ctxt);
if (bodyRaised != null)
{
throw bodyRaised;
}
}
return rv;
}
@Override
public Value caseAAssignmentStm(AAssignmentStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
Value newval = node.getExp().apply(VdmRuntime.getStatementEvaluator(), ctxt);
Value oldval = node.getTarget().apply(VdmRuntime.getStatementEvaluator(), ctxt);
// Experimental hood added for DESTECS
if (Settings.dialect == Dialect.VDM_RT)
{
SharedStateListner.beforeAssignmentSet(node, oldval, newval);
}
try
{
oldval.set(node.getLocation(), newval.convertTo(node.getTargetType(), ctxt), ctxt);
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
if (Settings.dialect == Dialect.VDM_RT
&& Properties.rt_log_instvarchanges)
{
ObjectValue self = ctxt.getSelf(); // May be a static
// The showtrace plugin does not like "quotes", nor does it
// have a \" type convention, so we substitute for apostrophes.
String noquotes = newval.toString().replaceAll("\\\"", "\'");
if (self == null)
{
RTLogger.log(new RTExtendedTextMessage("InstVarChange -> instnm: \""
+ node.getTarget().toString()
+ "\""
+ " val: \""
+ noquotes
+ "\""
+ " objref: nil"
+ " id: "
+ BasicSchedulableThread.getThread(Thread.currentThread()).getId()));
} else
{
RTLogger.log(new RTExtendedTextMessage("InstVarChange -> instnm: \""
+ node.getTarget().toString()
+ "\""
+ " val: \""
+ noquotes
+ "\""
+ " objref: "
+ self.objectReference
+ " id: "
+ BasicSchedulableThread.getThread(Thread.currentThread()).getId()));
}
}
return new VoidValue();
}
@Override
public Value caseAAtomicStm(AAtomicStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
int size = node.getAssignments().size();
ValueList targets = new ValueList(size);
ValueList values = new ValueList(size);
// Rather than execute the assignment statements directly, we calculate the
// Updateable values that would be affected, and the new values to put in them.
// Note that this does not provoke any invariant checks (other than those that
// may be present in the RHS expression of each assignment).
for (AAssignmentStm stmt : node.getAssignments())
{
try
{
stmt.getLocation().hit();
targets.add(stmt.getTarget().apply(VdmRuntime.getStatementEvaluator(), ctxt));
values.add(stmt.getExp().apply(VdmRuntime.getStatementEvaluator(), ctxt).convertTo(stmt.getTargetType(), ctxt));
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
}
// We make the assignments atomically by turning off thread swaps and time
// then temporarily removing the listener lists from each Updateable target.
// Then, when all assignments have been made, we check the invariants by
// passing the updated values to each listener list, as the assignment would have.
// Finally, we re-enable the thread swapping and time stepping, before returning
// a void value.
try
{
ctxt.threadState.setAtomic(true);
List<ValueListenerList> listenerLists = new Vector<ValueListenerList>(size);
for (int i = 0; i < size; i++)
{
UpdatableValue target = (UpdatableValue) targets.get(i);
listenerLists.add(target.listeners);
target.listeners = null;
target.set(node.getLocation(), values.get(i), ctxt); // No invariant listeners
target.listeners = listenerLists.get(i);
}
for (int i = 0; i < size; i++)
{
ValueListenerList listeners = listenerLists.get(i);
if (listeners != null)
{
listeners.changedValue(node.getLocation(), values.get(i), ctxt);
}
}
} finally
{
ctxt.threadState.setAtomic(false);
}
return new VoidValue();
}
@Override
public Value caseACallObjectStm(ACallObjectStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
node.getField().getLocation().hit();
// The check above increments the hit counter for the call, but so
// do the evaluations of the designator below, so we correct the
// hit count here...
node.getLocation().setHits(node.getLocation().getHits() - 1);
// node.getLocation().hits--;
try
{
ValueList argValues = new ValueList();
for (PExp arg: node.getArgs())
{
argValues.add(arg.apply(VdmRuntime.getStatementEvaluator(), ctxt));
}
// Work out the actual types of the arguments, so we bind the right op/fn
List<PType> argTypes = new Vector<PType>();
int arg = 0;
for (PType argType: node.getField().getTypeQualifier())
{
if (argType instanceof AUnionType)
{
AUnionType u = (AUnionType)argType;
for (PType possible: u.getTypes())
{
try
{
argValues.get(arg).convertTo(possible, ctxt);
argTypes.add(possible);
break;
}
catch (ValueException e)
{
// Try again
}
}
}
else
{
argTypes.add(argType);
}
arg++;
}
if (argTypes.size() != node.getField().getTypeQualifier().size())
{
VdmRuntimeError.abort(node.getField().getLocation(), 4168, "Arguments do not match parameters: " + node.getField(), ctxt);
}
else
{
node.setField(node.getField().getModifiedName(argTypes));
}
ObjectValue obj = node.getDesignator().apply(VdmRuntime.getStatementEvaluator(), ctxt).objectValue(ctxt);
Value v = obj.get(node.getField(), node.getExplicit());
if (v == null)
{
VdmRuntimeError.abort(node.getField().getLocation(), 4035, "Object has no field: "
+ node.getField().getName(), ctxt);
}
v = v.deref();
if (v instanceof OperationValue)
{
OperationValue op = v.operationValue(ctxt);
return op.eval(node.getLocation(), argValues, ctxt);
} else
{
FunctionValue op = v.functionValue(ctxt);
return op.eval(node.getLocation(), argValues, ctxt);
}
} catch (ValueException e)
{
return VdmRuntimeError.abort(node.getLocation(), e);
}
}
@Override
public Value caseACallStm(ACallStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
try
{
Value v = ctxt.lookup(node.getName()).deref();
if (v instanceof OperationValue)
{
OperationValue op = v.operationValue(ctxt);
ValueList argValues = new ValueList();
for (PExp arg : node.getArgs())
{
argValues.add(arg.apply(VdmRuntime.getStatementEvaluator(), ctxt));
}
return op.eval(node.getLocation(), argValues, ctxt);
} else
{
FunctionValue fn = v.functionValue(ctxt);
ValueList argValues = new ValueList();
for (PExp arg : node.getArgs())
{
argValues.add(arg.apply(VdmRuntime.getStatementEvaluator(), ctxt));
}
return fn.eval(node.getLocation(), argValues, ctxt);
}
} catch (ValueException e)
{
return VdmRuntimeError.abort(node.getLocation(), e);
}
}
@Override
public Value caseACasesStm(ACasesStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
Value val = node.getExp().apply(VdmRuntime.getStatementEvaluator(), ctxt);
for (ACaseAlternativeStm c : node.getCases())
{
Value rv = eval(c, val, ctxt);
if (rv != null)
{
return rv;
}
}
if (node.getOthers() != null)
{
return node.getOthers().apply(VdmRuntime.getStatementEvaluator(), ctxt);
}
return new VoidValue();
}
@Override
public Value caseAClassInvariantStm(AClassInvariantStm node, Context ctxt)
throws AnalysisException
{
for (PDefinition d : node.getInvDefs())
{
AClassInvariantDefinition invdef = (AClassInvariantDefinition) d;
try
{
if (!invdef.getExpression().apply(VdmRuntime.getStatementEvaluator(), ctxt).boolValue(ctxt))
{
return new BooleanValue(false);
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
}
return new BooleanValue(true);
}
@Override
public Value caseACyclesStm(ACyclesStm node, Context ctxt)
throws AnalysisException
{
node.getLocation().hit();
node.getCycles().getLocation().hit();
ISchedulableThread me = BasicSchedulableThread.getThread(Thread.currentThread());
if (me.inOuterTimestep())
{
// Already in a timed step, so ignore nesting
return node.getStatement().apply(VdmRuntime.getStatementEvaluator(), ctxt);
} else
{
// We disable the swapping and time (RT) as cycles evaluation should be "free".
Long cycles;
try
{
ctxt.threadState.setAtomic(true);
cycles = node.getCycles().apply(VdmRuntime.getStatementEvaluator(), ctxt).natValue(ctxt);
;
} finally
{
ctxt.threadState.setAtomic(false);
}
me.inOuterTimestep(true);
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), ctxt);
me.inOuterTimestep(false);
me.duration(ctxt.threadState.CPU.getDuration(cycles), ctxt, node.getLocation());
return rv;
}
}
@Override
public Value caseADurationStm(ADurationStm node, Context ctxt)
throws AnalysisException
{
node.getLocation().hit();
node.getDuration().getLocation().hit();
ISchedulableThread me = BasicSchedulableThread.getThread(Thread.currentThread());
if (me.inOuterTimestep())
{
// Already in a timed step, so ignore nesting
return node.getStatement().apply(VdmRuntime.getStatementEvaluator(), ctxt);
} else
{
// We disable the swapping and time (RT) as duration evaluation should be "free".
long step;
try
{
ctxt.threadState.setAtomic(true);
step = node.getDuration().apply(VdmRuntime.getStatementEvaluator(), ctxt).natValue(ctxt);
} finally
{
ctxt.threadState.setAtomic(false);
}
me.inOuterTimestep(true);
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), ctxt);
me.inOuterTimestep(false);
me.duration(step, ctxt, node.getLocation());
return rv;
}
}
@Override
public Value caseAElseIfStm(AElseIfStm node, Context ctxt)
throws AnalysisException
{
return evalElseIf(node, node.getLocation(), node.getElseIf(), node.getThenStm(), ctxt);
}
@Override
public Value caseAErrorStm(AErrorStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
return VdmRuntimeError.abort(node.getLocation(), 4036, "ERROR statement reached", ctxt);
}
@Override
public Value caseAExitStm(AExitStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
Value v = null;
if (Settings.release == Release.VDM_10 && ctxt.threadState.isPure())
{
VdmRuntimeError.abort(node.getLocation(), 4167, "Cannot call exit in a pure operation", ctxt);
}
if (node.getExpression() != null)
{
v = node.getExpression().apply(VdmRuntime.getStatementEvaluator(), ctxt);
} else
{
v = new UndefinedValue();
}
throw new ExitException(v, node.getLocation(), ctxt); // BANG!!
}
@Override
public Value caseAForAllStm(AForAllStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
try
{
ValueSet values = node.getSet().apply(VdmRuntime.getStatementEvaluator(), ctxt).setValue(ctxt);
for (Value val : values)
{
try
{
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "for all", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(node.getPattern(), val, ctxt));
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), evalContext);
if (!rv.isVoid())
{
return rv;
}
} catch (PatternMatchException e)
{
// Ignore and try others
}
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
return new VoidValue();
}
@Override
public Value caseAForIndexStm(AForIndexStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
try
{
long fval = node.getFrom().apply(VdmRuntime.getStatementEvaluator(), ctxt).intValue(ctxt);
long tval = node.getTo().apply(VdmRuntime.getStatementEvaluator(), ctxt).intValue(ctxt);
long bval = node.getBy() == null ? 1
: node.getBy().apply(VdmRuntime.getStatementEvaluator(), ctxt).intValue(ctxt);
if (bval == 0)
{
VdmRuntimeError.abort(node.getLocation(), 4038, "Loop, from "
+ fval + " to " + tval + " by " + bval
+ " will never terminate", ctxt);
}
for (long value = fval; bval > 0 && value <= tval || bval < 0
&& value >= tval; value += bval)
{
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "for index", ctxt);
evalContext.put(node.getVar(), new IntegerValue(value));
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), evalContext);
if (!rv.isVoid())
{
return rv;
}
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
return new VoidValue();
}
@Override
public Value caseAForPatternBindStm(AForPatternBindStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
try
{
ValueList values = node.getExp().apply(VdmRuntime.getStatementEvaluator(), ctxt).seqValue(ctxt);
if (node.getReverse())
{
ListIterator<Value> li = values.listIterator(values.size());
ValueList backwards = new ValueList();
while (li.hasPrevious())
{
backwards.add(li.previous());
}
values = backwards;
}
if (node.getPatternBind().getPattern() != null)
{
for (Value val : values)
{
try
{
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "for pattern", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(node.getPatternBind().getPattern(), val, ctxt));
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), evalContext);
if (!rv.isVoid())
{
return rv;
}
} catch (PatternMatchException e)
{
// Ignore mismatches
}
}
}
else if (node.getPatternBind().getBind() instanceof ASetBind)
{
ASetBind setbind = (ASetBind) node.getPatternBind().getBind();
ValueSet set = setbind.getSet().apply(VdmRuntime.getStatementEvaluator(), ctxt).setValue(ctxt);
for (Value val : values)
{
try
{
if (!set.contains(val))
{
VdmRuntimeError.abort(node.getLocation(), 4039, "Set bind does not contain value "
+ val, ctxt);
}
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "for set bind", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(setbind.getPattern(), val, ctxt));
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), evalContext);
if (!rv.isVoid())
{
return rv;
}
}
catch (PatternMatchException e)
{
// Ignore mismatches
}
}
}
else if (node.getPatternBind().getBind() instanceof ASeqBind)
{
ASeqBind seqbind = (ASeqBind) node.getPatternBind().getBind();
ValueList seq = seqbind.getSeq().apply(VdmRuntime.getStatementEvaluator(), ctxt).seqValue(ctxt);
for (Value val : values)
{
try
{
if (!seq.contains(val))
{
VdmRuntimeError.abort(node.getLocation(), 4039, "Seq bind does not contain value "
+ val, ctxt);
}
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "for seq bind", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(seqbind.getPattern(), val, ctxt));
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), evalContext);
if (!rv.isVoid())
{
return rv;
}
}
catch (PatternMatchException e)
{
// Ignore mismatches
}
}
}
else
{
ATypeBind typebind = (ATypeBind) node.getPatternBind().getBind();
for (Value val : values)
{
try
{
Value converted = val.convertTo(typebind.getType(), ctxt);
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "for type bind", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(typebind.getPattern(), converted, ctxt));
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), evalContext);
if (!rv.isVoid())
{
return rv;
}
} catch (PatternMatchException e)
{
// Ignore mismatches
}
}
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
return new VoidValue();
}
@Override
public Value caseAIfStm(AIfStm node, Context ctxt) throws AnalysisException
{
return evalIf(node, node.getLocation(), node.getIfExp(), node.getThenStm(), node.getElseIf(), node.getElseStm(), ctxt);
}
@Override
public Value caseALetBeStStm(ALetBeStStm node, Context ctxt)
throws AnalysisException
{
return evalLetBeSt(node, node.getLocation(), node.getDef(), node.getSuchThat(), node.getStatement(), 4040, "statement", ctxt);
}
@Override
public Value caseALetStm(ALetStm node, Context ctxt)
throws AnalysisException
{
return evalLet(node, node.getLocation(), node.getLocalDefs(), node.getStatement(), "statement", ctxt);
}
@Override
public Value caseANotYetSpecifiedStm(ANotYetSpecifiedStm node, Context ctxt)
throws AnalysisException
{
return evalANotYetSpecified(node,node.getLocation(),4041,"statement", ctxt);
}
@Override
public Value caseAReturnStm(AReturnStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
if (node.getExpression() == null)
{
return new VoidReturnValue();
} else
{
return node.getExpression().apply(VdmRuntime.getStatementEvaluator(), ctxt);
}
}
@Override
public Value caseABlockSimpleBlockStm(ABlockSimpleBlockStm node,
Context ctxt) throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "block statement", ctxt);
for (PDefinition d : node.getAssignmentDefs())
{
evalContext.putList(ctxt.assistantFactory.createPDefinitionAssistant().getNamedValues(d, evalContext));
}
return this.evalBlock(node, evalContext);
}
@Override
public Value caseANonDeterministicSimpleBlockStm(
ANonDeterministicSimpleBlockStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
return this.evalBlock(node, ctxt);
}
@Override
public Value caseASkipStm(ASkipStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
return new VoidValue();
}
@Override
public Value caseASpecificationStm(ASpecificationStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
return VdmRuntimeError.abort(node.getLocation(), 4047, "Cannot execute specification statement", ctxt);
}
@Override
public Value caseAStartStm(AStartStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
try
{
Value value = node.getObj().apply(VdmRuntime.getStatementEvaluator(), ctxt);
if (value.isType(SetValue.class))
{
ValueSet set = value.setValue(ctxt);
for (Value v : set)
{
ObjectValue target = v.objectValue(ctxt);
OperationValue op = target.getThreadOperation(ctxt);
start(node, target, op, ctxt);
}
} else
{
ObjectValue target = value.objectValue(ctxt);
OperationValue op = target.getThreadOperation(ctxt);
start(node, target, op, ctxt);
}
return new VoidValue();
} catch (ValueException e)
{
return VdmRuntimeError.abort(node.getLocation(), e);
}
}
@Override
public Value caseAStopStm(AStopStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
try
{
Value value = node.getObj().apply(VdmRuntime.getStatementEvaluator(), ctxt);
if (value.isType(SetValue.class))
{
ValueSet set = value.setValue(ctxt);
for (Value v : set)
{
ObjectValue target = v.objectValue(ctxt);
stop(target, node.getLocation(), ctxt);
}
} else
{
ObjectValue target = value.objectValue(ctxt);
stop(target, node.getLocation(), ctxt);
}
// Cause a reschedule so that this thread is stopped, if necessary
ISchedulableThread th = BasicSchedulableThread.getThread(Thread.currentThread());
th.reschedule(ctxt, node.getLocation());
return new VoidValue();
} catch (ValueException e)
{
return VdmRuntimeError.abort(node.getLocation(), e);
}
}
private void stop(ObjectValue target, ILexLocation location, Context ctxt)
throws ValueException
{
List<ISchedulableThread> threads = BasicSchedulableThread.findThreads(target);
int count = 0;
if (target.getCPU() != ctxt.threadState.CPU)
{
throw new ContextException(4161, "Cannot stop object "
+ target.objectReference + " on CPU "
+ target.getCPU().getName() + " from CPU "
+ ctxt.threadState.CPU, location, ctxt);
}
for (ISchedulableThread th : threads)
{
if (th instanceof ObjectThread || th instanceof PeriodicThread)
{
if (th.stopThread()) // This may stop current thread at next reschedule
{
count++;
}
}
}
if (count == 0)
{
throw new ContextException(4160, "Object #"
+ target.objectReference
+ " is not running a thread to stop", location, ctxt);
}
}
@Override
public Value caseASubclassResponsibilityStm(
ASubclassResponsibilityStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
return VdmRuntimeError.abort(node.getLocation(), 4048, "'is subclass responsibility' statement reached", ctxt);
}
@Override
public Value caseATixeStm(ATixeStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
Value rv = null;
try
{
rv = node.getBody().apply(VdmRuntime.getStatementEvaluator(), ctxt);
} catch (ExitException original)
{
ExitException last = original;
while (true)
{
Value exval = last.value;
try
{
for (ATixeStmtAlternative tsa : node.getTraps())
{
rv = //ctxt.assistantFactory.createATixeStmtAlternativeAssistant().
eval(tsa, node.getLocation(), exval, ctxt);
if (rv != null) // Statement was executed
{
return rv;
}
}
} catch (ExitException ex)
{
last = ex;
continue;
}
throw last;
}
}
return rv;
}
@Override
public Value caseATrapStm(ATrapStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
Value rv = null;
try
{
rv = node.getBody().apply(VdmRuntime.getStatementEvaluator(), ctxt);
} catch (ExitException e)
{
Value exval = e.value;
try
{
if (node.getPatternBind().getPattern() != null)
{
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "trap pattern", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(node.getPatternBind().getPattern(), exval, ctxt));
rv = node.getWith().apply(VdmRuntime.getStatementEvaluator(), evalContext);
}
else if (node.getPatternBind().getBind() instanceof ASetBind)
{
ASetBind setbind = (ASetBind) node.getPatternBind().getBind();
ValueSet set = setbind.getSet().apply(VdmRuntime.getStatementEvaluator(), ctxt).setValue(ctxt);
if (set.contains(exval))
{
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "trap set", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(setbind.getPattern(), exval, ctxt));
rv = node.getWith().apply(VdmRuntime.getStatementEvaluator(), evalContext);
}
else
{
VdmRuntimeError.abort(node.getLocation(), 4050, "Value "
+ exval + " is not in set bind", ctxt);
}
}
else if (node.getPatternBind().getBind() instanceof ASeqBind)
{
ASeqBind seqbind = (ASeqBind) node.getPatternBind().getBind();
ValueList seq = seqbind.getSeq().apply(VdmRuntime.getStatementEvaluator(), ctxt).seqValue(ctxt);
if (seq.contains(exval))
{
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "trap seq", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(seqbind.getPattern(), exval, ctxt));
rv = node.getWith().apply(VdmRuntime.getStatementEvaluator(), evalContext);
}
else
{
VdmRuntimeError.abort(node.getLocation(), 4050, "Value "
+ exval + " is not in seq bind", ctxt);
}
}
else
{
ATypeBind typebind = (ATypeBind) node.getPatternBind().getBind();
Value converted = exval.convertTo(typebind.getType(), ctxt);
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "trap type", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(typebind.getPattern(), converted, ctxt));
rv = node.getWith().apply(VdmRuntime.getStatementEvaluator(), evalContext);
}
}
catch (ValueException ve)
{
VdmRuntimeError.abort(node.getLocation(), ve);
}
catch (PatternMatchException pe)
{
throw e;
}
}
return rv;
}
@Override
public Value caseAWhileStm(AWhileStm node, Context ctxt)
throws AnalysisException
{
BreakpointManager.getBreakpoint(node).check(node.getLocation(), ctxt);
try
{
while (node.getExp().apply(VdmRuntime.getStatementEvaluator(), ctxt).boolValue(ctxt))
{
Value rv = node.getStatement().apply(VdmRuntime.getStatementEvaluator(), ctxt);
if (!rv.isVoid())
{
return rv;
}
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
return new VoidValue();
}
@Override
public Value caseAPeriodicStm(APeriodicStm node, Context ctxt)
throws AnalysisException
{
final int PERIODIC = 0;
final int JITTER = 1;
final int DELAY = 2;
final int OFFSET = 3;
node.setPeriod(0L);
node.setJitter(0L);
node.setDelay(0L);
node.setOffset(0L);
for (int i = 0; i < node.getArgs().size(); i++)
{
PExp arg = node.getArgs().get(i);
long value = -1;
Value argval = null;
try
{
arg.getLocation().hit();
try
{
// We disable the swapping and time (RT) as periodic evaluation should be "free".
ctxt.threadState.setAtomic(true);
ctxt.threadState.setPure(true);
argval = arg.apply(VdmRuntime.getExpressionEvaluator(), ctxt);
value = argval.intValue(ctxt);
}
finally
{
ctxt.threadState.setAtomic(false);
ctxt.threadState.setPure(false);
}
if (value < 0)
{
VdmRuntimeError.abort(node.getLocation(), 4157, "Expecting +ive integer in periodic argument "
+ (i + 1) + ", was " + value, ctxt);
}
if (i == PERIODIC)
{
node.setPeriod(value);
} else if (i == JITTER)
{
node.setJitter(value);
} else if (i == DELAY)
{
node.setDelay(value);
} else if (i == OFFSET)
{
node.setOffset(value);
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), 4157, "Expecting +ive integer in periodic argument "
+ (i + 1) + ", was " + argval, ctxt);
}
}
if (node.getPeriod() == 0)
{
VdmRuntimeError.abort(node.getLocation(), 4158, "Period argument must be non-zero, was "
+ node.getPeriod(), ctxt);
}
if (node.getArgs().size() == 4)
{
if (node.getDelay() >= node.getPeriod())
{
VdmRuntimeError.abort(node.getLocation(), 4159, "Delay argument ("
+ node.getDelay()
+ ") must be less than the period ("
+ node.getPeriod() + ")", ctxt);
}
}
return null; // Not actually used - see StartStatement
}
@Override
public Value caseASporadicStm(ASporadicStm node, Context ctxt)
throws AnalysisException
{
final int MINDELAY = 0;
final int MAXDELAY = 1;
final int OFFSET = 2;
node.setMinDelay(0L);
node.setMaxDelay(0L);
node.setOffset(0L);
int i = 0;
for (PExp arg : node.getArgs())
{
Value argval = null;
long value = 0;
try
{
arg.getLocation().hit();
try
{
// We disable the swapping and time (RT) as periodic evaluation should be "free".
ctxt.threadState.setAtomic(true);
ctxt.threadState.setPure(true);
argval = arg.apply(VdmRuntime.getExpressionEvaluator(), ctxt);
value = argval.intValue(ctxt);
}
finally
{
ctxt.threadState.setAtomic(false);
ctxt.threadState.setPure(false);
}
if (value < 0)
{
VdmRuntimeError.abort(node.getLocation(), 4157, "Expecting +ive integer in sporadic argument "
+ (i + 1) + ", was " + value, ctxt);
}
if (i == MINDELAY)
{
node.setMinDelay(value);
} else if (i == MAXDELAY)
{
node.setMaxDelay(value);
} else if (i == OFFSET)
{
node.setOffset(value);
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), 4157, "Expecting +ive integer in sporadic argument "
+ (i + 1) + ", was " + argval, ctxt);
}
i++;
}
return null; // Not actually used - see StartStatement
}
@Override
public Value caseAIdentifierStateDesignator(
AIdentifierStateDesignator node, Context ctxt)
throws AnalysisException
{
// We lookup the name in a context comprising only state...
// return ctxt.getUpdateable().lookup(name.getExplicit(true));
return ctxt.lookup(node.getName().getExplicit(true));
}
@Override
public Value caseAFieldStateDesignator(AFieldStateDesignator node,
Context ctxt) throws AnalysisException
{
Value result = null;
try
{
result = node.getObject().apply(VdmRuntime.getStatementEvaluator(), ctxt).deref();
if (result instanceof ObjectValue && node.getObjectfield() != null)
{
ObjectValue ov = result.objectValue(ctxt);
Value rv = ov.get(node.getObjectfield(), false);
if (rv == null)
{
VdmRuntimeError.abort(node.getLocation(), 4045, "Object does not contain value for field: "
+ node.getField(), ctxt);
}
return rv;
} else if (result instanceof RecordValue)
{
RecordValue rec = result.recordValue(ctxt);
result = rec.fieldmap.get(node.getField().getName());
if (result == null)
{
VdmRuntimeError.abort(node.getField().getLocation(), 4037, "No such field: "
+ node.getField(), ctxt);
}
return result;
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
return result;
}
@Override
public Value caseAFieldObjectDesignator(AFieldObjectDesignator node,
Context ctxt) throws AnalysisException
{
try
{
Value val = node.getObject().apply(VdmRuntime.getStatementEvaluator(), ctxt).deref();
if (val instanceof ObjectValue && node.getField() != null)
{
ObjectValue ov = val.objectValue(ctxt);
Value rv = ov.get(node.getField(), node.getClassName() != null);
if (rv == null)
{
VdmRuntimeError.abort(node.getLocation(), 4045, "Object does not contain value for field: "
+ node.getField(), ctxt);
}
return rv;
} else if (val instanceof RecordValue)
{
RecordValue rec = val.recordValue(ctxt);
Value result = rec.fieldmap.get(node.getFieldName().getName());
if (result == null)
{
VdmRuntimeError.abort(node.getLocation(), 4046, "No such field: "
+ node.getFieldName(), ctxt);
}
return result;
} else
{
return VdmRuntimeError.abort(node.getLocation(), 4020, "State value is neither a record nor an object", ctxt);
}
} catch (ValueException e)
{
return VdmRuntimeError.abort(node.getLocation(), e);
}
}
@Override
public Value caseAMapSeqStateDesignator(AMapSeqStateDesignator node,
Context ctxt) throws AnalysisException
{
Value result = null;
try
{
Value root = node.getMapseq().apply(VdmRuntime.getStatementEvaluator(), ctxt);
Value index = node.getExp().apply(VdmRuntime.getExpressionEvaluator(), ctxt);
if (root.isType(MapValue.class))
{
index = index.convertTo(node.getMapType().getFrom(), ctxt);
ValueMap map = root.mapValue(ctxt);
result = map.get(index);
if (result == null && root instanceof UpdatableValue)
{
// Assignment to a non-existent map key creates the value
// in order to have it updated.
UpdatableValue ur = (UpdatableValue) root;
result = UpdatableValue.factory(ur.listeners, node.getMapType().getTo());
map.put(index, result);
}
} else if (root.isType(SeqValue.class))
{
ValueList seq = root.seqValue(ctxt);
int i = (int) index.intValue(ctxt) - 1;
if (!seq.inbounds(i))
{
if (i == seq.size())
{
// Assignment to an index one greater than the length
// creates the value in order to have it updated.
UpdatableValue ur = (UpdatableValue) root;
seq.add(UpdatableValue.factory(ur.listeners, node.getSeqType().getSeqof()));
} else
{
VdmRuntimeError.abort(node.getExp().getLocation(), 4019, "Sequence cannot extend to key: "
+ index, ctxt);
}
}
result = seq.get(i);
} else
{
VdmRuntimeError.abort(node.getLocation(), 4020, "State value is neither a sequence nor a map", ctxt);
}
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
return result;
}
@Override
public Value caseAApplyObjectDesignator(AApplyObjectDesignator node,
Context ctxt) throws AnalysisException
{
try
{
Value uv = node.getObject().apply(VdmRuntime.getStatementEvaluator(), ctxt);
Value v = uv.deref();
if (v instanceof MapValue)
{
ValueMap mv = v.mapValue(ctxt);
Value a = node.getArgs().get(0).apply(VdmRuntime.getExpressionEvaluator(), ctxt);
Value rv = mv.get(a);
if (rv == null && uv instanceof UpdatableValue)
{
// Not already in map - get listener from root object
UpdatableValue ur = (UpdatableValue) uv;
rv = UpdatableValue.factory(ur.listeners);
mv.put(a, rv);
}
return rv;
} else if (v instanceof SeqValue)
{
ValueList seq = v.seqValue(ctxt);
Value a = node.getArgs().get(0).apply(VdmRuntime.getExpressionEvaluator(), ctxt);
int i = (int) a.intValue(ctxt) - 1;
if (!seq.inbounds(i))
{
VdmRuntimeError.abort(node.getLocation(), 4042, "Sequence does not contain key: "
+ a, ctxt);
}
return seq.get(i);
} else if (v instanceof FunctionValue)
{
ValueList argvals = new ValueList();
for (PExp arg : node.getArgs())
{
argvals.add(arg.apply(VdmRuntime.getExpressionEvaluator(), ctxt));
}
FunctionValue fv = v.functionValue(ctxt);
return fv.eval(node.getLocation(), argvals, ctxt);
} else if (v instanceof OperationValue)
{
ValueList argvals = new ValueList();
for (PExp arg : node.getArgs())
{
argvals.add(arg.apply(VdmRuntime.getExpressionEvaluator(), ctxt));
}
OperationValue ov = v.operationValue(ctxt);
return ov.eval(node.getLocation(), argvals, ctxt);
} else
{
return VdmRuntimeError.abort(node.getLocation(), 4043, "Object designator is not a map, sequence, operation or function", ctxt);
}
} catch (ValueException e)
{
return VdmRuntimeError.abort(node.getLocation(), e);
}
}
@Override
public Value caseAIdentifierObjectDesignator(
AIdentifierObjectDesignator node, Context ctxt)
throws AnalysisException
{
return node.getExpression().apply(VdmRuntime.getExpressionEvaluator(), ctxt);
}
@Override
public Value caseANewObjectDesignator(ANewObjectDesignator node,
Context ctxt) throws AnalysisException
{
return node.getExpression().apply(VdmRuntime.getExpressionEvaluator(), ctxt);
}
@Override
public Value caseASelfObjectDesignator(ASelfObjectDesignator node,
Context ctxt) throws AnalysisException
{
return ctxt.lookup(node.getSelf());
}
private Value evalBlock(SSimpleBlockStm node, Context ctxt)
throws AnalysisException
{
// Note, no breakpoint check - designed to be called by eval
for (PStm s : node.getStatements())
{
Value rv = s.apply(VdmRuntime.getStatementEvaluator(), ctxt);
if (!rv.isVoid())
{
return rv;
}
}
return new VoidValue();
}
public Value eval(ATixeStmtAlternative node, ILexLocation location,
Value exval, Context ctxt) throws AnalysisException
{
Context evalContext = null;
try
{
if (node.getPatternBind().getPattern() != null)
{
evalContext = new Context(ctxt.assistantFactory, location, "tixe pattern", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(node.getPatternBind().getPattern(), exval, ctxt));
}
else if (node.getPatternBind().getBind() instanceof ASetBind)
{
ASetBind setbind = (ASetBind) node.getPatternBind().getBind();
ValueSet set = setbind.getSet().apply(VdmRuntime.getStatementEvaluator(), ctxt).setValue(ctxt);
if (set.contains(exval))
{
evalContext = new Context(ctxt.assistantFactory, location, "tixe set", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(setbind.getPattern(), exval, ctxt));
}
else
{
VdmRuntimeError.abort(setbind.getLocation(), 4049, "Value "
+ exval + " is not in set bind", ctxt);
}
}
else if (node.getPatternBind().getBind() instanceof ASeqBind)
{
ASeqBind seqbind = (ASeqBind) node.getPatternBind().getBind();
ValueList seq = seqbind.getSeq().apply(VdmRuntime.getStatementEvaluator(), ctxt).seqValue(ctxt);
if (seq.contains(exval))
{
evalContext = new Context(ctxt.assistantFactory, location, "tixe seq", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(seqbind.getPattern(), exval, ctxt));
}
else
{
VdmRuntimeError.abort(seqbind.getLocation(), 4049, "Value "
+ exval + " is not in seq bind", ctxt);
}
}
else
{
ATypeBind typebind = (ATypeBind) node.getPatternBind().getBind();
// Note we always perform DTC checks here...
Value converted = exval.convertValueTo(typebind.getType(), ctxt);
evalContext = new Context(ctxt.assistantFactory, location, "tixe type", ctxt);
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(typebind.getPattern(), converted, ctxt));
}
} catch (ValueException ve) // Type bind convert failure
{
evalContext = null;
} catch (PatternMatchException e)
{
evalContext = null;
}
return evalContext == null ? null
: node.getStatement().apply(VdmRuntime.getStatementEvaluator(), evalContext);
}
private Value eval(ACaseAlternativeStm node, Value val, Context ctxt)
throws AnalysisException
{
Context evalContext = new Context(ctxt.assistantFactory, node.getLocation(), "case alternative", ctxt);
node.getPattern().getLocation().hit();
node.getLocation().hit();
try
{
evalContext.putList(ctxt.assistantFactory.createPPatternAssistant().getNamedValues(node.getPattern(), val, ctxt));
return node.getResult().apply(VdmRuntime.getStatementEvaluator(), evalContext);
} catch (PatternMatchException e)
{
// CasesStatement tries the others
}
return null;
}
public void start(AStartStm node, ObjectValue target, OperationValue op,
Context ctxt) throws AnalysisException
{
if (op.body instanceof APeriodicStm)
{
RootContext global = ClassInterpreter.getInstance().initialContext;
Context pctxt = new ObjectContext(ctxt.assistantFactory, op.name.getLocation(), "async", global, target);
APeriodicStm ps = (APeriodicStm) op.body;
// We disable the swapping and time (RT) as periodic evaluation should be "free".
try
{
pctxt.threadState.setAtomic(true);
ps.apply(VdmRuntime.getStatementEvaluator(), pctxt); // Ignore return value
} finally
{
pctxt.threadState.setAtomic(false);
}
OperationValue pop = pctxt.lookup(ps.getOpname()).operationValue(pctxt);
long period = ps.getPeriod();
long jitter = ps.getJitter();
long delay = ps.getDelay();
long offset = ps.getOffset();
// Note that periodic threads never set the stepping flag
new PeriodicThread(target, pop, period, jitter, delay, offset, 0, false).start();
} else if (op.body instanceof ASporadicStm)
{
RootContext global = ClassInterpreter.getInstance().initialContext;
Context pctxt = new ObjectContext(ctxt.assistantFactory, op.name.getLocation(), "sporadic", global, target);
ASporadicStm ss = (ASporadicStm) op.body;
// We disable the swapping and time (RT) as sporadic evaluation should be "free".
try
{
pctxt.threadState.setAtomic(true);
ss.apply(VdmRuntime.getStatementEvaluator(), pctxt); // Ignore return value
} finally
{
pctxt.threadState.setAtomic(false);
}
OperationValue pop = pctxt.lookup(ss.getOpname()).operationValue(pctxt);
long delay = ss.getMinDelay();
long jitter = ss.getMaxDelay(); // Jitter used for maximum delay
long offset = ss.getOffset();
long period = 0;
// Note that periodic threads never set the stepping flag
new PeriodicThread(target, pop, period, jitter, delay, offset, 0, true).start();
} else
{
new ObjectThread(node.getLocation(), target, ctxt).start();
}
}
}