/*******************************************************************************
*
* Copyright (C) 2008 Fujitsu Services Ltd.
*
* Author: Nick Battle
*
* This file is part of VDMJ.
*
* VDMJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VDMJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VDMJ. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
package org.overture.interpreter.runtime;
import java.io.PrintWriter;
import org.overture.ast.intf.lex.ILexLocation;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.interpreter.assistant.IInterpreterAssistantFactory;
import org.overture.interpreter.debug.DBGPReader;
import org.overture.interpreter.values.CPUValue;
import org.overture.interpreter.values.NameValuePair;
import org.overture.interpreter.values.NameValuePairList;
import org.overture.interpreter.values.ObjectValue;
import org.overture.interpreter.values.OperationValue;
import org.overture.interpreter.values.Value;
import org.overture.typechecker.util.LexNameTokenMap;
/**
* A class to hold runtime name/value context information.
*
* @author Nick, modified by Kenneth Lausdahl
*/
@SuppressWarnings("serial")
public class Context extends LexNameTokenMap<Value>
{
/** Debug flag used to include extra information in toString */
public final static boolean DEBUG = true;
/** The running debug id of contexts */
private static int nextid = 0;
/** cache for transient serialization */
private static final CloneTrancientMemoryCache<ThreadState> serilizationCache = new CloneTrancientMemoryCache<ThreadState>();
/** The debug if of this context */
protected final int id;
/** The Assistant factory used by this context */
public final IInterpreterAssistantFactory assistantFactory;
/** The location of the context. */
public final ILexLocation location;
/** The name of the location. */
public final String title;
/** A link to a lower level context, if present. */
public final Context outer;
/** The thread state associated with this context. */
public transient ThreadState threadState = null;
/** Serialization key for the transient threadState **/
private Integer threadStateSerilizationHashedId = null;
/** Non-zero if this is a pre or postcondition call. */
public int prepost = 0;
/** Set to the error message if prepost is set. */
public String prepostMsg = null;
/** Set to the operation being guarded, if any. */
public OperationValue guardOp = null;
/**
* Create a context at the given location.
*
* @param af
* @param location
* @param title
* @param outer
*/
public Context(IInterpreterAssistantFactory af, ILexLocation location,
String title, Context outer)
{
id = nextid++;
this.assistantFactory = af;
this.location = location;
this.outer = outer;
this.title = title;
if (outer != null)
{
this.threadState = outer.threadState;
}
}
/**
* Set the current thread state. Note this must be called from the thread where the context will run, which may not
* be where the thread is created. And it must be called before any context chaining is performed.
*
* @param dbgp
* @param cpu
* TODO
*/
public void setThreadState(DBGPReader dbgp, CPUValue cpu)
{
threadState = new ThreadState(dbgp, cpu);
}
/**
* Find the outermost context from this one.
*
* @return The outermost context.
*/
public Context getGlobal()
{
Context op = this;
while (op.outer != null)
{
op = op.outer;
}
return op;
}
/**
* Find the nearest RootContext in the context chain.
*
* @return
*/
public RootContext getRoot()
{
assert outer != null : "Root context is wrong type";
return outer.getRoot(); // RootContext overrides this!
}
/**
* Make a deep copy of the context, using Value.deepCopy. Every concrete subclass must implements its own version of
* this method.
*
* @return
*/
public Context deepCopy()
{
Context below = null;
if (outer != null)
{
below = outer.deepCopy();
}
Context result = new Context(assistantFactory, location, title, below);
result.threadState = threadState;
for (ILexNameToken var : keySet())
{
Value v = get(var);
result.put(var, v.deepCopy());
}
return result;
}
/**
* Add a list of name/value pairs to this context.
*
* @param nvl
* A list of name/value pairs.
*/
public void putList(NameValuePairList nvl)
{
for (NameValuePair nv : nvl)
{
put(nv.name, nv.value);
}
}
public void putNew(NameValuePair nvp)
{
if (get(nvp.name) == null)
{
put(nvp.name, nvp.value);
}
}
public void putAllNew(NameValuePairList list)
{
for (NameValuePair nvp : list)
{
putNew(nvp);
}
}
/**
* Get a name, taking type overloading into account. If we use the superclass method, different names are considered
* different, because the map is driven by the names' hashCodes. The equals method of LexNameToken makes a
* TypeComparator check, which is what we need. But we try a simple super.get() first. TODO Slow though.
*/
@Override
public Value get(Object name)
{
Value rv = super.get(name);
if (rv == null)
{
for (ILexNameToken var : keySet())
{
if (assistantFactory.getLexNameTokenAssistant().isEqual(var, name))
{
rv = super.get(var);
break;
}
}
}
return rv;
}
/**
* Get all visible names from this Context, with more visible values overriding those below.
*
* @return A new Context with all visible names.
*/
public Context getVisibleVariables()
{
Context visible = new Context(assistantFactory, location, title, null);
if (outer != null)
{
visible.putAll(outer.getVisibleVariables());
}
visible.putAll(this); // Overriding anything below here
return visible;
}
/**
* Get the value for a given name. This searches outer contexts, if any are present.
*
* @param name
* The name to look for.
* @return The value of the name, or null.
*/
public Value check(ILexNameToken name)
{
Value v = get(name);
if (v == null)
{
if (outer != null)
{
return outer.check(name);
}
}
return v;
}
/**
* Locate the Context in a chain that contains a name, if any.
*
* @param name
* @return
*/
public Context locate(ILexNameToken name)
{
Value v = get(name);
if (v == null)
{
if (outer != null)
{
return outer.locate(name);
} else
{
return null;
}
} else
{
return this;
}
}
/**
* Return the value of a name, else fail. If the name is not present, a {@link ContextException} is thrown.
*
* @param name
* The name to look for.
* @return The value of the name.
*/
public Value lookup(ILexNameToken name)
{
Value v = check(name);
if (v == null)
{
VdmRuntimeError.abort(name.getLocation(), 4034, "Name '" + name
+ "' not in scope", this);
}
return v;
}
@Override
public String toString()
{
return (DEBUG ? "#" + id + " " : "") + format("", this)
+ "-------------------\n" + (outer == null ? "" : outer.toString());
}
protected String format(String indent, Context what)
{
StringBuilder sb = new StringBuilder();
for (ILexNameToken name : what.keySet())
{
sb.append(indent + name + " = " + what.get(name).toShortString(100)
+ "\n");
}
return sb.toString();
}
public void printStackTrace(PrintWriter out, boolean variables)
{
if (outer == null) // Don't expand initial context
{
out.println("In context of " + title);
} else
{
if (variables)
{
out.print(this.format("\t", this));
}
out.println("In context of " + title + " " + location);
outer.printStackTrace(out, variables);
}
}
public int getDepth()
{
return outer == null ? 0 : outer.getDepth(); // NB only roots count
}
public Context getFrame(int depth)
{
return outer == null ? this : outer.getFrame(depth);
}
public ObjectValue getSelf()
{
return outer == null ? null : outer.getSelf();
}
public void setPrepost(int prepost, String prepostMsg)
{
this.prepost = prepost;
this.prepostMsg = prepostMsg;
}
/**
* Custom serialization method handling the transient values
*
* @param stream
* @throws java.io.IOException
*/
private synchronized void writeObject(java.io.ObjectOutputStream stream)
throws java.io.IOException
{
ThreadState tmp = this.threadState;
this.threadStateSerilizationHashedId = serilizationCache.store(this.threadState);
this.threadState = null;
stream.defaultWriteObject();
this.threadState = tmp;
}
/**
* Custom de-serialization method handling the transient values
*
* @param stream
* @throws java.io.IOException
* @throws ClassNotFoundException
*/
private synchronized void readObject(java.io.ObjectInputStream stream)
throws java.io.IOException, ClassNotFoundException
{
stream.defaultReadObject();
this.threadState = (ThreadState) serilizationCache.load(this.threadStateSerilizationHashedId);
this.threadStateSerilizationHashedId = null;
}
}