package org.overture.interpreter.assistant.definition;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.assistant.IAstAssistant;
import org.overture.ast.assistant.pattern.PTypeList;
import org.overture.ast.definitions.ABusClassDefinition;
import org.overture.ast.definitions.AClassClassDefinition;
import org.overture.ast.definitions.AClassInvariantDefinition;
import org.overture.ast.definitions.ACpuClassDefinition;
import org.overture.ast.definitions.AInheritedDefinition;
import org.overture.ast.definitions.AMutexSyncDefinition;
import org.overture.ast.definitions.APerSyncDefinition;
import org.overture.ast.definitions.APrivateAccess;
import org.overture.ast.definitions.AProtectedAccess;
import org.overture.ast.definitions.APublicAccess;
import org.overture.ast.definitions.ASystemClassDefinition;
import org.overture.ast.definitions.PAccess;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.expressions.PExp;
import org.overture.ast.factory.AstFactory;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.lex.Dialect;
import org.overture.ast.lex.LexIntegerToken;
import org.overture.ast.lex.LexKeywordToken;
import org.overture.ast.lex.LexNameList;
import org.overture.ast.lex.LexNameToken;
import org.overture.ast.lex.LexToken;
import org.overture.ast.lex.VDMToken;
import org.overture.ast.statements.PStm;
import org.overture.ast.typechecker.NameScope;
import org.overture.ast.types.AClassType;
import org.overture.config.Settings;
import org.overture.interpreter.assistant.IInterpreterAssistantFactory;
import org.overture.interpreter.runtime.Context;
import org.overture.interpreter.runtime.ObjectContext;
import org.overture.interpreter.runtime.StateContext;
import org.overture.interpreter.runtime.ValueException;
import org.overture.interpreter.runtime.VdmRuntime;
import org.overture.interpreter.runtime.VdmRuntimeError;
import org.overture.interpreter.util.ClassListInterpreter;
import org.overture.interpreter.values.BUSValue;
import org.overture.interpreter.values.CPUValue;
import org.overture.interpreter.values.ClassInvariantListener;
import org.overture.interpreter.values.MapValue;
import org.overture.interpreter.values.NameValuePair;
import org.overture.interpreter.values.NameValuePairList;
import org.overture.interpreter.values.NameValuePairMap;
import org.overture.interpreter.values.ObjectValue;
import org.overture.interpreter.values.OperationValue;
import org.overture.interpreter.values.SeqValue;
import org.overture.interpreter.values.UpdatableValue;
import org.overture.interpreter.values.Value;
import org.overture.interpreter.values.ValueList;
import org.overture.interpreter.values.ValueMap;
import org.overture.pog.contexts.POContextStack;
import org.overture.pog.obligation.ProofObligationList;
import org.overture.typechecker.assistant.definition.SClassDefinitionAssistantTC;
public class SClassDefinitionAssistantInterpreter extends
SClassDefinitionAssistantTC implements IAstAssistant
{
protected static IInterpreterAssistantFactory af;
@SuppressWarnings("static-access")
public SClassDefinitionAssistantInterpreter(IInterpreterAssistantFactory af)
{
super(af);
this.af = af;
}
public Value getStatic(SClassDefinition classdef, ILexNameToken sought)
{
ILexNameToken local = sought.getExplicit() ? sought
: sought.getModifiedName(classdef.getName().getName());
Value v = VdmRuntime.getNodeState(af, classdef).privateStaticValues.get(local);
if (v == null)
{
v = VdmRuntime.getNodeState(af, classdef).publicStaticValues.get(local);
if (v == null)
{
for (SClassDefinition sdef : classdef.getSuperDefs())
{
v = getStatic(sdef, local);
if (v != null)
{
break;
}
}
}
}
return v;
}
public Context getStatics(SClassDefinition classdef)
{
Context ctxt = new Context(af, classdef.getLocation(), "Statics", null);
ctxt.putAll(VdmRuntime.getNodeState(af, classdef).publicStaticValues);
ctxt.putAll(VdmRuntime.getNodeState(af, classdef).privateStaticValues);
return ctxt;
}
public MapValue getOldValues(SClassDefinition classdef, LexNameList oldnames)
{
ValueMap values = new ValueMap();
for (ILexNameToken name : oldnames)
{
Value mv = getStatic(classdef, name.getNewName()).deref();
SeqValue sname = new SeqValue(name.getName());
if (mv instanceof ObjectValue)
{
ObjectValue om = (ObjectValue) mv;
values.put(sname, om.deepCopy());
} else
{
values.put(sname, (Value) mv.clone());
}
}
return new MapValue(values);
}
// TODO:rewrite this???
public ObjectValue newInstance(SClassDefinition node,
PDefinition ctorDefinition, ValueList argvals, Context ctxt)
throws AnalysisException
{
if (node instanceof ABusClassDefinition)
{
return newInstance((ABusClassDefinition) node, ctorDefinition, argvals, ctxt);
} else if (node instanceof AClassClassDefinition)
{
if (node.getIsAbstract())
{
VdmRuntimeError.abort(node.getLocation(), 4000, "Cannot instantiate abstract class "
+ node.getName(), ctxt);
}
return af.createSClassDefinitionAssistant().makeNewInstance(node, ctorDefinition, argvals, ctxt, new HashMap<ILexNameToken, ObjectValue>(), false);
//return af.createAClassClassDefinitionAssistant().newInstance((AClassClassDefinition) node, ctorDefinition, argvals, ctxt);
} else if (node instanceof ACpuClassDefinition)
{
return af.createACpuClassDefinitionAssistant().newInstance((ACpuClassDefinition) node, ctorDefinition, argvals, ctxt);
} else if (node instanceof ASystemClassDefinition)
{
VdmRuntimeError.abort(node.getLocation(), 4135, "Cannot instantiate system class "
+ node.getName(), ctxt);
}
return null;
}
public ObjectValue makeNewInstance(SClassDefinition node,
PDefinition ctorDefinition, ValueList argvals, Context ctxt,
Map<ILexNameToken, ObjectValue> done, boolean nested)
throws AnalysisException
{
setStaticDefinitions(node, ctxt.getGlobal()); // When static member := new X()
setStaticValues(node, ctxt.getGlobal()); // When static member := new X()
List<ObjectValue> inherited = new Vector<ObjectValue>();
NameValuePairMap members = new NameValuePairMap();
for (SClassDefinition sdef : node.getSuperDefs())
{
// Check the "done" list for virtual inheritance
ObjectValue obj = done.get(sdef.getName());
if (obj == null)
{
obj = makeNewInstance(sdef, null, null, ctxt, done, true);
done.put(sdef.getName(), obj);
}
inherited.add(obj);
}
// NB. we don't use localInheritedDefinitions because we're creating
// the local definitions in this loop.
for (PDefinition idef : node.getSuperInheritedDefinitions())
{
// Inherited definitions don't notice when their referenced
// definition names are updated with type qualifiers.
// FIXME This ought to be done at the end of type checking...
if (idef instanceof AInheritedDefinition)
{
AInheritedDefinition i = (AInheritedDefinition) idef;
i.getName().setTypeQualifier(i.getSuperdef().getName().getTypeQualifier());
}
if (af.createPDefinitionAssistant().isRuntime(idef) && // eg. TypeDefinitions aren't
!af.createPDefinitionAssistant().isSubclassResponsibility(idef))
{
Value v = null;
for (ObjectValue sobj : inherited)
{
v = sobj.get(idef.getName(), true);
if (v != null)
{
ILexNameToken localname = idef.getName().getModifiedName(node.getName().getName());
// In a cascade of classes all overriding a name, we may
// have already created the local name for the nearest
// name - superInheritedDefinitions has the nearest first.
if (members.get(localname) == null)
{
members.put(localname, v);
}
break;
}
}
if (v == null)
{
VdmRuntimeError.abort(node.getLocation(), 6, "Constructor for "
+ node.getName().getName()
+ " can't find "
+ idef.getName(), ctxt);
}
}
}
members.putAll(VdmRuntime.getNodeState(af, node).publicStaticValues);
members.putAll(VdmRuntime.getNodeState(af, node).privateStaticValues);
// We create a RootContext here so that the scope for member
// initializations are restricted.
Context initCtxt = new StateContext(af, node.getLocation(), "field initializers", ctxt, null);
initCtxt.putList(members.asList());
// We create an empty context to pass for function creation, so that
// there are no spurious free variables created.
Context empty = new Context(af, node.getLocation(), "empty", null);
NameValuePairMap inheritedNames = new NameValuePairMap();
inheritedNames.putAll(members); // Not a clone
for (PDefinition d : node.getDefinitions())
{
if (!af.createPDefinitionAssistant().isStatic(d)
&& af.createPDefinitionAssistant().isFunctionOrOperation(d))
{
NameValuePairList nvpl = af.createPDefinitionAssistant().getNamedValues(d, empty);
for (NameValuePair nvp: nvpl)
{
// If there are already overloads inherited, we have to remove them because
// any local names hide all inherited overloads (like C++).
for (ILexNameToken iname: inheritedNames.getOverloadNames(nvp.name))
{
initCtxt.remove(iname);
members.remove(iname);
inheritedNames.remove(iname);
}
initCtxt.put(nvp.name, nvp.value);
members.put(nvp.name, nvp.value);
}
}
}
for (PDefinition d : node.getDefinitions())
{
if (!af.createPDefinitionAssistant().isStatic(d)
&& !af.createPDefinitionAssistant().isFunctionOrOperation(d))
{
NameValuePairList nvpl = af.createPDefinitionAssistant().getNamedValues(d, initCtxt).getUpdatable(null);
initCtxt.putList(nvpl);
members.putAll(nvpl);
}
}
setPermissions(node, node.getDefinitions(), members, initCtxt);
setPermissions(node, node.getSuperInheritedDefinitions(), members, initCtxt);
ObjectValue creator = ctxt.outer == null ? null : ctxt.outer.getSelf();
ObjectValue object = new ObjectValue((AClassType) af.createSClassDefinitionAssistant().getType(node), members, inherited, ctxt.threadState.CPU, creator);
Value ctor = null;
if (ctorDefinition == null)
{
argvals = new ValueList();
LexNameToken constructor = getCtorName(node, new PTypeList());
ctorDefinition = af.createPDefinitionAssistant().findName(node, constructor, NameScope.NAMES);
if (ctorDefinition != null)
{
ctor = object.get(ctorDefinition.getName(), false);
}
} else
{
ctor = object.get(ctorDefinition.getName(), false);
}
if (Settings.dialect == Dialect.VDM_RT)
{
CPUValue cpu = object.getCPU();
if (cpu != null)
{
cpu.deploy(object);
}
}
if (ctor != null) // Class may have no constructor defined
{
OperationValue ov = ctor.operationValue(ctxt);
ObjectContext ctorCtxt = new ObjectContext(af, ov.name.getLocation(), node.getName().getName()
+ " constructor", ctxt, object);
if (ctorDefinition.getAccess().getAccess() instanceof APrivateAccess
&& nested)
{
VdmRuntimeError.abort(ctorDefinition.getLocation(), 4163, "Cannot inherit private constructor", ctorCtxt);
}
ov.eval(ov.name.getLocation(), argvals, ctorCtxt);
}
// Do invariants and guards after construction, so values fields are set. The
// invariant does not apply during construction anyway.
if (node.getInvariant() != null)
{
OperationValue invop = new OperationValue(node.getInvariant(), null, null, null, af);
ClassInvariantListener listener = new ClassInvariantListener(invop);
for (PDefinition d : af.createSClassDefinitionAssistant().getInvDefs(node))
{
AClassInvariantDefinition inv = (AClassInvariantDefinition) d;
// Is this correct?
ValueList values = af.createPExpAssistant().getValues(inv.getExpression(), new ObjectContext(af, node.getLocation(), node.getName().getName()
+ " object context", initCtxt, object));
for (Value v : values)
{
UpdatableValue uv = (UpdatableValue) v;
uv.addListener(listener);
}
}
object.setListener(listener);
}
if (VdmRuntime.getNodeState(af, node).hasPermissions)
{
ObjectContext self = new ObjectContext(af, node.getLocation(), node.getName().getName()
+ " guards", ctxt, object);
for (Entry<ILexNameToken, Value> entry : members.entrySet())
{
Value v = entry.getValue();
if (v instanceof OperationValue)
{
OperationValue opv = (OperationValue) v;
opv.prepareGuard(self);
}
}
}
return object;
}
private void setPermissions(SClassDefinition node,
LinkedList<PDefinition> definitions, NameValuePairMap members,
Context initCtxt) throws ValueException
{
for (PDefinition d : node.getDefinitions())
{
while (d instanceof AInheritedDefinition)
{
d = ((AInheritedDefinition) d).getSuperdef();
}
if (d instanceof APerSyncDefinition)
{
APerSyncDefinition sync = (APerSyncDefinition) d;
ValueList overloads = members.getOverloads(sync.getOpname());
PExp exp = sync.getGuard();
for (Value op : overloads)
{
op.operationValue(initCtxt).setGuard(exp, false);
}
VdmRuntime.getNodeState(af, node).hasPermissions = true;
} else if (d instanceof AMutexSyncDefinition)
{
AMutexSyncDefinition sync = (AMutexSyncDefinition) d;
for (ILexNameToken opname : new LexNameList(sync.getOperations()))
{
PExp exp = getExpression(sync.clone(), opname);
ValueList overloads = members.getOverloads(opname);
for (Value op : overloads)
{
op.operationValue(initCtxt).setGuard(exp, true);
}
}
VdmRuntime.getNodeState(af, node).hasPermissions = true;
}
}
}
private void setStaticValues(SClassDefinition node, Context initCtxt)
{
if (!VdmRuntime.getNodeState(af, node).staticValuesInit)
{
VdmRuntime.getNodeState(af, node).staticValuesInit = true;
for (SClassDefinition sdef : node.getSuperDefs())
{
setStaticValues(sdef, initCtxt);
}
setStaticValues(node, node.getLocalInheritedDefinitions(), initCtxt, true);
setStaticValues(node, node.getDefinitions(), initCtxt, false);
}
}
private void setStaticValues(SClassDefinition node,
LinkedList<PDefinition> defs, Context initCtxt, boolean inherit)
{
for (PDefinition d : defs)
{
NameValuePairList nvl = null;
if (inherit)
{
AInheritedDefinition id = (AInheritedDefinition) d;
LexNameList names = af.createPDefinitionAssistant().getVariableNames(d);
nvl = new NameValuePairList();
for (ILexNameToken vname : names)
{
ILexNameToken iname = vname.getModifiedName(id.getSuperdef().getName().getModule());
Value v = initCtxt.check(iname);
if (v != null) // TypeDefinition names aren't values
{
nvl.add(vname, v);
}
}
} else
{
if (af.createPDefinitionAssistant().isValueDefinition(d))
{
nvl = af.createPDefinitionAssistant().getNamedValues(d, initCtxt);
} else if (af.createPDefinitionAssistant().isStatic(d)
&& af.createPDefinitionAssistant().isInstanceVariable(d))
{
nvl = af.createPDefinitionAssistant().getNamedValues(d, initCtxt);
}
}
if (af.createPDefinitionAssistant().isValueDefinition(d))
{
// Values are implicitly static, but NOT updatable
PAccess pAccess = d.getAccess().getAccess();
if (pAccess instanceof APrivateAccess
|| pAccess instanceof AProtectedAccess)
{
VdmRuntime.getNodeState(af, node).privateStaticValues.putAllNew(nvl);
initCtxt.putAllNew(nvl);
} else if (pAccess instanceof APublicAccess)
{
VdmRuntime.getNodeState(af, node).publicStaticValues.putAllNew(nvl);
initCtxt.putAllNew(nvl);
}
} else if (af.createPDefinitionAssistant().isStatic(d)
&& af.createPDefinitionAssistant().isInstanceVariable(d))
{
// Static instance variables are updatable
PAccess pAccess = d.getAccess().getAccess();
if (pAccess instanceof APrivateAccess
|| pAccess instanceof AProtectedAccess)
{
VdmRuntime.getNodeState(af, node).privateStaticValues.putAllNew(nvl);
initCtxt.putAllNew(nvl);
} else if (pAccess instanceof APublicAccess)
{
VdmRuntime.getNodeState(af, node).publicStaticValues.putAllNew(nvl);
initCtxt.putAllNew(nvl);
}
}
}
}
private void setStaticDefinitions(SClassDefinition node, Context initCtxt)
{
if (!VdmRuntime.getNodeState(af, node).staticInit)
{
VdmRuntime.getNodeState(af, node).staticInit = true;
for (SClassDefinition sdef : node.getSuperDefs())
{
setStaticDefinitions(sdef, initCtxt);
}
VdmRuntime.getNodeState(af, node).privateStaticValues = new NameValuePairMap();
VdmRuntime.getNodeState(af, node).publicStaticValues = new NameValuePairMap();
// We initialize function and operation definitions first as these
// can be called by variable initializations.
setStaticDefinitions(node, node.getDefinitions(), initCtxt);
setStaticDefinitions(node, node.getLocalInheritedDefinitions(), initCtxt);
try
{
NameValuePairMap members = new NameValuePairMap();
members.putAll(VdmRuntime.getNodeState(af, node).privateStaticValues);
members.putAll(VdmRuntime.getNodeState(af, node).publicStaticValues);
af.createSClassDefinitionAssistant().setPermissions(node, node.getDefinitions(), members, initCtxt);
} catch (ValueException e)
{
VdmRuntimeError.abort(node.getLocation(), e);
}
}
}
private void setStaticDefinitions(SClassDefinition node,
LinkedList<PDefinition> defs, Context initCtxt)
{
for (PDefinition d : defs)
{
if (af.createPDefinitionAssistant().isStatic(d)
&& af.createPDefinitionAssistant().isFunctionOrOperation(d)
|| af.createPDefinitionAssistant().isTypeDefinition(d))
{
// Note function and operation values are not updatable.
// Type invariants are implicitly static, but not updatable
// The context here is just used for free variables, of
// which there are none at static func/op creation...
Context empty = new Context(af, node.getLocation(), "empty", null);
NameValuePairList nvl = af.createPDefinitionAssistant().getNamedValues(d, empty);
PAccess pAccess = d.getAccess().getAccess();
if (pAccess instanceof APrivateAccess
|| pAccess instanceof AProtectedAccess)
{
VdmRuntime.getNodeState(af, node).privateStaticValues.putAllNew(nvl);
initCtxt.putList(nvl);
} else if (pAccess instanceof APublicAccess)
{
VdmRuntime.getNodeState(af, node).publicStaticValues.putAllNew(nvl);
initCtxt.putList(nvl);
}
}
}
}
public void staticInit(SClassDefinition cdef, StateContext ctxt)
{
VdmRuntime.getNodeState(af, cdef).staticInit = false; // Forced initialization
VdmRuntime.getNodeState(af, cdef).staticValuesInit = false; // Forced initialization
VdmRuntime.getNodeState(af, cdef).privateStaticValues = new NameValuePairMap();
VdmRuntime.getNodeState(af, cdef).publicStaticValues = new NameValuePairMap();
setStaticDefinitions(cdef, ctxt);
}
public ProofObligationList getProofObligations(SClassDefinition c,
POContextStack ctxt)
{
return af.createPDefinitionListAssistant().getProofObligations(c.getDefinitions(), ctxt);
}
public void staticValuesInit(SClassDefinition cdef, StateContext ctxt)
{
VdmRuntime.getNodeState(af, cdef).staticValuesInit = false; // Forced initialization
setStaticValues(cdef, ctxt);
}
public boolean hasDelegate(SClassDefinition classdef)
{
return VdmRuntime.getNodeState(af, classdef).hasDelegate();
}
public Object newInstance(SClassDefinition classdef)
{
return VdmRuntime.getNodeState(af, classdef).newInstance();
}
public Value invokeDelegate(SClassDefinition classdef,
Object delegateObject, Context ctxt)
{
return VdmRuntime.getNodeState(af, classdef).invokeDelegate(delegateObject, ctxt);
}
public PExp findExpression(SClassDefinition d, int lineno)
{
return af.createPDefinitionListAssistant().findExpression(d.getDefinitions(), lineno);
}
// public static boolean isTypeDefinition(SClassDefinition def)
// {
// return true;
// }
public PStm findStatement(ClassListInterpreter classes, File file,
int lineno)
{
for (SClassDefinition c : classes)
{
if (c.getName().getLocation().getFile().equals(file))
{
PStm stmt = findStatement(c, lineno);
if (stmt != null)
{
return stmt;
}
}
}
return null;
}
public PStm findStatement(SClassDefinition c, int lineno)
{
return af.createPDefinitionAssistant().findStatement(c.getDefinitions(), lineno);
}
public PExp findExpression(ClassListInterpreter classes, File file,
int lineno)
{
for (SClassDefinition c : classes)
{
if (c.getName().getLocation().getFile().equals(file))
{
PExp exp = af.createSClassDefinitionAssistant().findExpression(c, lineno);
if (exp != null)
{
return exp;
}
}
}
return null;
}
public ObjectValue newInstance(ABusClassDefinition node,
PDefinition ctorDefinition, ValueList argvals, Context ctxt)
{
NameValuePairList nvpl = af.createPDefinitionListAssistant().getNamedValues(node.getDefinitions(), ctxt);
NameValuePairMap map = new NameValuePairMap();
map.putAll(nvpl);
return new BUSValue((AClassType) node.getClasstype(), map, argvals);
}
public PExp getExpression(AMutexSyncDefinition sync, ILexNameToken excluding)
{
LexNameList list = null;
if (sync.getOperations().size() == 1)
{
list = new LexNameList();
list.addAll(sync.getOperations());
} else
{
list = new LexNameList();
list.addAll(sync.getOperations());
list.remove(excluding);
}
return AstFactory.newAEqualsBinaryExp(AstFactory.newAHistoryExp(sync.getLocation(), new LexToken(sync.getLocation(), VDMToken.ACTIVE), list), new LexKeywordToken(VDMToken.EQUALS, sync.getLocation()), AstFactory.newAIntLiteralExp(new LexIntegerToken(0, sync.getLocation())));
}
}