/*******************************************************************************
*
* Copyright (c) 2009 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.debug;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.definitions.AMutexSyncDefinition;
import org.overture.ast.definitions.APerSyncDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.expressions.AHistoryExp;
import org.overture.ast.expressions.PExp;
import org.overture.ast.factory.AstFactory;
import org.overture.ast.intf.lex.ILexLocation;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.lex.Dialect;
import org.overture.ast.lex.LexLocation;
import org.overture.ast.lex.LexLocationUtils;
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.messages.InternalException;
import org.overture.ast.modules.AModuleModules;
import org.overture.ast.statements.PStm;
import org.overture.ast.util.definitions.ClassList;
import org.overture.config.Release;
import org.overture.config.Settings;
import org.overture.interpreter.VDMJ;
import org.overture.interpreter.VDMPP;
import org.overture.interpreter.VDMRT;
import org.overture.interpreter.VDMSL;
import org.overture.interpreter.messages.Console;
import org.overture.interpreter.messages.rtlog.RTLogger;
import org.overture.interpreter.messages.rtlog.RTTextLogger;
import org.overture.interpreter.messages.rtlog.nextgen.NextGenRTLogger;
import org.overture.interpreter.runtime.Breakpoint;
import org.overture.interpreter.runtime.ClassContext;
import org.overture.interpreter.runtime.ClassInterpreter;
import org.overture.interpreter.runtime.Context;
import org.overture.interpreter.runtime.ContextException;
import org.overture.interpreter.runtime.Interpreter;
import org.overture.interpreter.runtime.LatexSourceFile;
import org.overture.interpreter.runtime.ModuleInterpreter;
import org.overture.interpreter.runtime.ObjectContext;
import org.overture.interpreter.runtime.SourceFile;
import org.overture.interpreter.runtime.StateContext;
import org.overture.interpreter.runtime.VdmRuntime;
import org.overture.interpreter.scheduler.BasicSchedulableThread;
import org.overture.interpreter.util.ExitStatus;
import org.overture.interpreter.values.CPUValue;
import org.overture.interpreter.values.NameValuePairMap;
import org.overture.interpreter.values.Value;
import org.overture.parser.config.Properties;
import org.overture.parser.lex.LexException;
import org.overture.parser.lex.LexTokenReader;
import org.overture.parser.syntax.ParserException;
import org.overture.pog.obligation.ProofObligationList;
import org.overture.pog.pub.IProofObligation;
import org.overture.pog.pub.IProofObligationList;
import org.overture.util.Base64;
public class DBGPReader
{
protected final String host;
protected final int port;
protected final String ideKey;
protected final String expression;
protected Socket socket;
protected InputStream input;
protected OutputStream output;
protected final Interpreter interpreter;
protected final CPUValue cpu;
protected int sessionId = 0;
protected DBGPStatus status = null;
protected DBGPReason statusReason = null;
protected DBGPCommandType command = null;
protected String transaction = "";
protected DBGPFeatures features;
protected byte separator = '\0';
protected Context breakContext = null;
protected Breakpoint breakpoint = null;
protected Value theAnswer = null;
protected boolean breaksSuspended = false;
protected boolean connected = false;
protected RemoteControl remoteControl = null;
protected boolean stopped = false;
protected boolean errorState = false;
protected static final int SOURCE_LINES = 5;
protected static List<DBGPReader> connectecReaders = new Vector<DBGPReader>();
@SuppressWarnings("unchecked")
public static void main(String[] args)
{
Settings.usingDBGP = true;
String host = null;
int port = -1;
String ideKey = null;
Settings.dialect = null;
String expression = null;
List<File> files = new Vector<File>();
List<String> largs = Arrays.asList(args);
VDMJ controller = null;
boolean warnings = true;
boolean quiet = false;
String logfile = null;
boolean expBase64 = false;
File coverage = null;
String defaultName = null;
String remoteName = null;
Class<RemoteControl> remoteClass = null;
Properties.init(); // Read properties file, if any
for (Iterator<String> i = largs.iterator(); i.hasNext();)
{
String arg = i.next();
if (arg.equals("-vdmsl"))
{
controller = new VDMSL();
} else if (arg.equals("-vdmpp"))
{
controller = new VDMPP();
} else if (arg.equals("-vdmrt"))
{
controller = new VDMRT();
} else if (arg.equals("-h"))
{
if (i.hasNext())
{
host = i.next();
} else
{
usage("-h option requires a hostname");
}
} else if (arg.equals("-p"))
{
try
{
port = Integer.parseInt(i.next());
} catch (Exception e)
{
usage("-p option requires a port");
}
} else if (arg.equals("-k"))
{
if (i.hasNext())
{
ideKey = i.next();
} else
{
usage("-k option requires a key");
}
} else if (arg.equals("-e"))
{
if (i.hasNext())
{
expression = i.next();
} else
{
usage("-e option requires an expression");
}
} else if (arg.equals("-e64"))
{
if (i.hasNext())
{
expression = i.next();
expBase64 = true;
} else
{
usage("-e64 option requires an expression");
}
} else if (arg.equals("-c"))
{
if (i.hasNext())
{
if (controller == null)
{
usage("-c must come after <-vdmpp|-vdmsl|-vdmrt>");
}
controller.setCharset(validateCharset(i.next()));
} else
{
usage("-c option requires a charset name");
}
} else if (arg.equals("-r"))
{
if (i.hasNext())
{
Settings.release = Release.lookup(i.next());
if (Settings.release == null)
{
usage("-r option must be " + Release.list());
}
} else
{
usage("-r option requires a VDM release");
}
} else if (arg.equals("-pre"))
{
Settings.prechecks = false;
} else if (arg.equals("-post"))
{
Settings.postchecks = false;
} else if (arg.equals("-inv"))
{
Settings.invchecks = false;
} else if (arg.equals("-dtc"))
{
// NB. Turn off both when no DTC
Settings.invchecks = false;
Settings.dynamictypechecks = false;
} else if (arg.equals("-measures"))
{
Settings.measureChecks = false;
} else if (arg.equals("-log"))
{
if (i.hasNext())
{
try
{
logfile = new URI(i.next()).getPath();
} catch (URISyntaxException e)
{
usage(e.getMessage() + ": " + arg);
} catch (IllegalArgumentException e)
{
usage(e.getMessage() + ": " + arg);
}
} else
{
usage("-log option requires a filename");
}
} else if (arg.equals("-w"))
{
warnings = false;
} else if (arg.equals("-q"))
{
quiet = true;
} else if (arg.equals("-coverage"))
{
if (i.hasNext())
{
try
{
coverage = new File(new URI(i.next()));
if (!coverage.isDirectory())
{
usage("Coverage location is not a directory");
}
} catch (URISyntaxException e)
{
usage(e.getMessage() + ": " + arg);
} catch (IllegalArgumentException e)
{
usage(e.getMessage() + ": " + arg);
}
} else
{
usage("-coverage option requires a directory name");
}
} else if (arg.equals("-default64"))
{
if (i.hasNext())
{
defaultName = i.next();
} else
{
usage("-default64 option requires a name");
}
} else if (arg.equals("-remote"))
{
if (i.hasNext())
{
remoteName = i.next();
} else
{
usage("-remote option requires a Java classname");
}
} else if (arg.equals("-consoleName"))
{
if (i.hasNext())
{
LexTokenReader.consoleFileName = i.next();
} else
{
usage("-consoleName option requires a console name");
}
} else if (arg.startsWith("-"))
{
usage("Unknown option " + arg);
} else
{
try
{
File dir = new File(new URI(arg));
if (dir.isDirectory())
{
for (File file : dir.listFiles(Settings.dialect.getFilter()))
{
if (file.isFile())
{
files.add(file);
}
}
} else
{
files.add(dir);
}
} catch (URISyntaxException e)
{
usage(e.getMessage() + ": " + arg);
} catch (IllegalArgumentException e)
{
usage(e.getMessage() + ": " + arg);
}
}
}
if (host == null || port == -1 || controller == null || ideKey == null
|| expression == null || Settings.dialect == null
|| files.isEmpty())
{
usage("Missing mandatory arguments");
}
if (Settings.dialect != Dialect.VDM_RT && logfile != null)
{
usage("-log can only be used with -vdmrt");
}
if (expBase64)
{
try
{
byte[] bytes = Base64.decode(expression);
expression = new String(bytes, VDMJ.filecharset);
} catch (Exception e)
{
usage("Malformed -e64 base64 expression");
}
}
if (defaultName != null)
{
try
{
byte[] bytes = Base64.decode(defaultName);
defaultName = new String(bytes, VDMJ.filecharset);
} catch (Exception e)
{
usage("Malformed -default64 base64 name");
}
}
if (remoteName != null)
{
try
{
Class<?> cls = ClassLoader.getSystemClassLoader().loadClass(remoteName);
remoteClass = (Class<RemoteControl>) cls;
} catch (ClassNotFoundException e)
{
usage("Cannot locate " + remoteName + " on the CLASSPATH");
}
}
controller.setWarnings(warnings);
controller.setQuiet(quiet);
if (controller.parse(files) == ExitStatus.EXIT_OK)
{
if (controller.typeCheck() == ExitStatus.EXIT_OK)
{
try
{
if (logfile != null)
{
RTLogger.setLogfile(RTTextLogger.class, new File(logfile));
RTLogger.setLogfile(NextGenRTLogger.class, new File(logfile));
}
Interpreter i = controller.getInterpreter();
if (defaultName != null)
{
i.setDefaultName(defaultName);
}
RemoteControl remote = remoteClass == null ? null
: remoteClass.newInstance();
new DBGPReader(host, port, ideKey, i, expression, null).startup(remote);
if (coverage != null)
{
writeCoverage(i, coverage);
}
RTLogger.dump(true);
System.exit(0);
} catch (ContextException e)
{
System.out.println("Initialization: " + e);
e.ctxt.printStackTrace(Console.out, true);
RTLogger.dump(true);
System.exit(3);
} catch (Exception e)
{
System.out.println("Initialization: " + e);
RTLogger.dump(true);
System.exit(3);
}
} else
{
System.exit(2);
}
} else
{
System.exit(1);
}
}
protected static void usage(String string)
{
System.err.println(string);
System.err.println("Usage: -h <host> -p <port> -k <ide key> <-vdmpp|-vdmsl|-vdmrt>"
+ " -e <expression> | -e64 <base64 expression>"
+ " [-w] [-q] [-log <logfile URL>] [-c <charset>] [-r <release>]"
+ " [-pre] [-post] [-inv] [-dtc] [-measures]"
+ " [-coverage <dir URL>] [-default64 <base64 name>]"
+ " [-remote <class>] [-consoleName <console>] [-baseDir <File>] {<filename URLs>}");
System.exit(1);
}
protected static String validateCharset(String cs)
{
if (!Charset.isSupported(cs))
{
System.err.println("Charset " + cs + " is not supported\n");
System.err.println("Available charsets:");
System.err.println("Default = " + Charset.defaultCharset());
Map<String, Charset> available = Charset.availableCharsets();
for (Entry<String, Charset> name : available.entrySet())
{
System.err.println(name.getKey() + " "
+ name.getValue().aliases());
}
System.err.println("");
usage("Charset " + cs + " is not supported");
}
return cs;
}
public DBGPReader(String host, int port, String ideKey,
Interpreter interpreter, String expression, CPUValue cpu)
{
this.host = host;
this.port = port;
this.ideKey = ideKey;
this.expression = expression;
this.interpreter = interpreter;
this.cpu = cpu;
}
public DBGPReader newThread(CPUValue _cpu)
{
DBGPReader r = new DBGPReader(host, port, ideKey, interpreter, null, _cpu);
r.command = DBGPCommandType.UNKNOWN;
r.transaction = "?";
return r;
}
protected void connect() throws IOException
{
if (!connected)
{
if (port > 0)
{
InetAddress server = InetAddress.getByName(host);
socket = new Socket(server, port);
input = socket.getInputStream();
output = socket.getOutputStream();
} else
{
socket = null;
input = System.in;
output = System.out;
separator = ' ';
}
connected = true;
addThisReader();
init();
run(); // New threads wait for a "run -i"
}
}
protected void startup(RemoteControl remote) throws IOException
{
remoteControl = remote; // Main thread only
interpreter.init(this);
connect();
}
protected void init() throws IOException
{
sessionId = Math.abs(new Random().nextInt(1000000));
status = DBGPStatus.STARTING;
statusReason = DBGPReason.OK;
features = new DBGPFeatures();
StringBuilder sb = new StringBuilder();
sb.append("<init ");
sb.append("appid=\"");
sb.append(features.getProperty(DBGPFeatures.LANGUAGE_NAME));
sb.append("\" ");
sb.append("idekey=\"" + ideKey + "\" ");
sb.append("session=\"" + sessionId + "\" ");
sb.append("thread=\"");
String threadName = BasicSchedulableThread.getThreadName(Thread.currentThread());
if (threadName != null)
{
sb.append(threadName);
} else
{
sb.append(Thread.currentThread().getName());
}
if (cpu != null)
{
sb.append(" on ");
sb.append(cpu.getName());
}
sb.append("\" ");
sb.append("parent=\"");
sb.append(features.getProperty(DBGPFeatures.LANGUAGE_NAME));
sb.append("\" ");
sb.append("language=\"");
sb.append(features.getProperty(DBGPFeatures.LANGUAGE_NAME));
sb.append("\" ");
sb.append("protocol_version=\"");
sb.append(features.getProperty(DBGPFeatures.PROTOCOL_VERSION));
sb.append("\"");
Set<File> files = interpreter.getSourceFiles();
sb.append(" fileuri=\"");
sb.append(files.iterator().next().toURI()); // Any old one...
sb.append("\"");
sb.append("/>\n");
write(sb);
}
protected String readLine() throws IOException
{
try
{
StringBuilder line = new StringBuilder();
int c = input.read();
while (c != '\n' && c > 0)
{
if (c != '\r')
{
line.append((char) c); // Ignore CRs
}
c = input.read();
}
return line.length() == 0 && c == -1 ? null : line.toString();
} catch (SocketException e)
{
// If DBGP is stopped there is no guarantee that the IDE will be available
if (stopped)
{
return null;
} else
{
throw e;
}
}
}
protected void write(StringBuilder data) throws IOException
{
if (output == null)
{
// TODO: Handle the error in VDMJ, terminate?
System.err.println("Socket to IDE not valid.");
return;
}
byte[] header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>".getBytes("UTF-8");
byte[] body = data.toString().getBytes("UTF-8");
byte[] size = Integer.toString(header.length + body.length).getBytes("UTF-8");
output.write(size);
output.write(separator);
output.write(header);
output.write(body);
output.write(separator);
output.flush();
}
protected void response(StringBuilder hdr, StringBuilder body)
throws IOException
{
StringBuilder sb = new StringBuilder();
sb.append("<response command=\"");
sb.append(command);
sb.append("\"");
if (hdr != null)
{
sb.append(" ");
sb.append(hdr);
}
sb.append(" transaction_id=\"");
sb.append(transaction);
sb.append("\"");
if (body != null)
{
sb.append(">");
sb.append(body);
sb.append("</response>\n");
} else
{
sb.append("/>\n");
}
write(sb);
}
protected void errorResponse(DBGPErrorCode errorCode, String reason)
{
try
{
StringBuilder sb = new StringBuilder();
sb.append("<error code=\"");
sb.append(errorCode.value);
sb.append("\" apperr=\"\"><message>");
sb.append(quote(reason));
sb.append("</message></error>");
response(null, sb);
} catch (SocketException e)
{
// Do not report these since the socket connection is down.
} catch (IOException e)
{
throw new InternalException(29, "DBGP: " + reason);
}
}
protected void statusResponse() throws IOException
{
statusResponse(status, statusReason);
}
protected void statusResponse(DBGPStatus s, DBGPReason reason)
throws IOException
{
StringBuilder sb = new StringBuilder();
if (s == DBGPStatus.STOPPED)
{
stopped = true;
}
status = s;
statusReason = reason;
sb.append("status=\"");
sb.append(status);
sb.append("\"");
sb.append(" reason=\"");
sb.append(statusReason);
sb.append("\"");
response(sb, null);
}
protected StringBuilder breakpointResponse(Breakpoint bp)
{
StringBuilder sb = new StringBuilder();
sb.append("<breakpoint id=\"" + bp.number + "\"");
sb.append(" type=\"line\"");
sb.append(" state=\"" + (bp.isEnabled() ? "enabled" : "disabled")
+ "\"");
sb.append(" filename=\"" + bp.location.getFile().toURI() + "\"");
sb.append(" lineno=\"" + bp.location.getStartLine() + "\"");
sb.append(">");
if (bp.trace != null)
{
sb.append("<expression>" + quote(bp.trace) + "</expression>");
}
sb.append("</breakpoint>");
return sb;
}
protected StringBuilder stackResponse(ILexLocation location, int level)
{
StringBuilder sb = new StringBuilder();
sb.append("<stack level=\"" + level + "\"");
sb.append(" type=\"file\"");
sb.append(" filename=\"" + location.getFile().toURI() + "\"");
sb.append(" lineno=\"" + location.getStartLine() + "\"");
sb.append(" cmdbegin=\"" + location.getStartLine() + ":"
+ location.getStartPos() + "\"");
sb.append("/>");
return sb;
}
protected StringBuilder propertyResponse(NameValuePairMap vars,
DBGPContextType context) throws UnsupportedEncodingException
{
StringBuilder sb = new StringBuilder();
for (Entry<ILexNameToken, Value> e : vars.entrySet())
{
sb.append(propertyResponse(e.getKey(), e.getValue(), context));
}
return sb;
}
protected StringBuilder propertyResponse(ILexNameToken name, Value value,
DBGPContextType context) throws UnsupportedEncodingException
{
return propertyResponse(name.getName(), name.getExplicit(true).toString(), name.getModule(), value.toString());
}
protected StringBuilder propertyResponse(String name, String fullname,
String clazz, String value) throws UnsupportedEncodingException
{
StringBuilder sb = new StringBuilder();
sb.append("<property");
sb.append(" name=\"" + quote(name) + "\"");
sb.append(" fullname=\"" + quote(fullname) + "\"");
sb.append(" type=\"string\"");
sb.append(" classname=\"" + clazz + "\"");
sb.append(" constant=\"0\"");
sb.append(" children=\"0\"");
sb.append(" size=\"" + value.length() + "\"");
sb.append(" encoding=\"base64\"");
sb.append("><![CDATA[");
sb.append(Base64.encode(value.getBytes("UTF-8")));
sb.append("]]></property>");
return sb;
}
protected void cdataResponse(String msg) throws IOException
{
// Send back a CDATA response with a plain message
response(null, new StringBuilder("<![CDATA[" + quote(msg) + "]]>"));
}
protected static String quote(String in)
{
return in.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
}
protected void run() throws IOException
{
String line = null;
do
{
line = readLine();
} while (line != null && process(line));
}
public void stopped(Context ctxt, ILexLocation location)
{
stopped(ctxt, new Breakpoint(location));
}
public void stopped(Context ctxt, Breakpoint bp)
{
if (breaksSuspended)
{
return; // We're inside an eval command, so don't stop
}
try
{
connect();
breakContext = ctxt;
breakpoint = bp;
if (errorState)
{
statusResponse(DBGPStatus.BREAK, DBGPReason.ERROR);
} else
{
statusResponse(DBGPStatus.BREAK, DBGPReason.OK);
}
run();
breakContext = null;
breakpoint = null;
} catch (Exception e)
{
errorResponse(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
public void setErrorState()
{
errorState = true;
}
public void invocationError(Throwable e)
{
String message = e.getMessage();
if (e instanceof StackOverflowError)
{
message = "StackOverflowError:\n Try to increase Java Stack size by adding -Xss4M to the Virtual Machine running the debugger";// +getStackTrace(e));
}
errorResponse(DBGPErrorCode.INTERNAL_ERROR, message);
}
public void tracing(String display)
{
try
{
connect();
cdataResponse(display);
} catch (Exception e)
{
errorResponse(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
public void complete(DBGPReason reason, ContextException ctxt)
{
try
{
if (reason == DBGPReason.OK && !connected)
{
// We never connected and there's no problem so just complete...
} else
{
connect();
if (reason == DBGPReason.EXCEPTION && ctxt != null)
{
dyingThread(ctxt);
} else
{
statusResponse(DBGPStatus.STOPPED, reason);
}
}
} catch (IOException e)
{
try
{
errorResponse(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
} catch (Throwable th)
{
// Probably a shutdown race...
}
} finally
{
try
{
if (socket != null)
{
socket.close();
}
} catch (IOException e)
{
// ?
}
}
}
protected boolean process(String line)
{
boolean carryOn = true;
try
{
command = DBGPCommandType.UNKNOWN;
transaction = "?";
String[] parts = line.split("\\s+");
DBGPCommand c = parse(parts);
switch (c.type)
{
case STATUS:
processStatus(c);
break;
case FEATURE_GET:
processFeatureGet(c);
break;
case FEATURE_SET:
processFeatureSet(c);
break;
case RUN:
carryOn = processRun(c);
break;
case EVAL:
carryOn = processEval(c);
break;
case EXPR:
carryOn = processExpr(c);
break;
case STEP_INTO:
processStepInto(c);
carryOn = false;
break;
case STEP_OVER:
processStepOver(c);
carryOn = false;
break;
case STEP_OUT:
processStepOut(c);
carryOn = false;
break;
case STOP:
processStop(c);
carryOn = false;
break;
case BREAKPOINT_GET:
breakpointGet(c);
break;
case BREAKPOINT_SET:
breakpointSet(c);
break;
case BREAKPOINT_UPDATE:
breakpointUpdate(c);
break;
case BREAKPOINT_REMOVE:
breakpointRemove(c);
break;
case BREAKPOINT_LIST:
breakpointList(c);
break;
case STACK_DEPTH:
stackDepth(c);
break;
case STACK_GET:
stackGet(c);
break;
case CONTEXT_NAMES:
contextNames(c);
break;
case CONTEXT_GET:
contextGet(c);
break;
case PROPERTY_GET:
propertyGet(c);
break;
case SOURCE:
processSource(c);
break;
case STDOUT:
processStdout(c);
break;
case STDERR:
processStderr(c);
break;
case DETACH:
carryOn = false;
break;
case XCMD_OVERTURE_CMD:
processOvertureCmd(c);
break;
case PROPERTY_SET:
default:
errorResponse(DBGPErrorCode.NOT_AVAILABLE, c.type.value);
}
} catch (DBGPException e)
{
errorResponse(e.code, e.reason);
} catch (Throwable e)
{
errorResponse(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
return carryOn;
}
protected DBGPCommand parse(String[] parts) throws Exception
{
// "<type> [<options>] [-- <base64 args>]"
List<DBGPOption> options = new Vector<DBGPOption>();
String args = null;
boolean doneOpts = false;
boolean gotXID = false;
try
{
command = DBGPCommandType.lookup(parts[0]);
for (int i = 1; i < parts.length; i++)
{
if (doneOpts)
{
if (args != null)
{
throw new Exception("Expecting one base64 arg after '--'");
} else
{
args = parts[i];
}
} else
{
if (parts[i].equals("--"))
{
doneOpts = true;
} else
{
DBGPOptionType opt = DBGPOptionType.lookup(parts[i++]);
if (opt == DBGPOptionType.TRANSACTION_ID)
{
gotXID = true;
transaction = parts[i];
}
options.add(new DBGPOption(opt, parts[i]));
}
}
}
if (!gotXID)
{
throw new Exception("No transaction_id");
}
} catch (DBGPException e)
{
throw e;
} catch (ArrayIndexOutOfBoundsException e)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, "Option arg missing");
} catch (Exception e)
{
if (doneOpts)
{
throw new DBGPException(DBGPErrorCode.PARSE, e.getMessage());
} else
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, e.getMessage());
}
}
return new DBGPCommand(command, options, args);
}
protected void checkArgs(DBGPCommand c, int n, boolean data)
throws DBGPException
{
if (data && c.data == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
if (c.options.size() != n)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
}
protected void processStatus(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 1, false);
statusResponse();
}
protected void processFeatureGet(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 2, false);
DBGPOption option = c.getOption(DBGPOptionType.N);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
String feature = features.getProperty(option.value);
StringBuilder hdr = new StringBuilder();
StringBuilder body = new StringBuilder();
if (feature == null)
{
// Unknown feature - unsupported in header; nothing in body
hdr.append("feature_name=\"");
hdr.append(option.value);
hdr.append("\" supported=\"0\"");
} else
{
// Known feature - supported in header; body reflects actual support
hdr.append("feature_name=\"");
hdr.append(option.value);
hdr.append("\" supported=\"1\"");
body.append(feature);
}
response(hdr, body);
}
protected void processFeatureSet(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 3, false);
DBGPOption option = c.getOption(DBGPOptionType.N);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
String feature = features.getProperty(option.value);
if (feature == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
DBGPOption newval = c.getOption(DBGPOptionType.V);
if (newval == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
features.setProperty(option.value, newval.value);
StringBuilder hdr = new StringBuilder();
hdr.append("feature_name=\"");
hdr.append(option.value);
hdr.append("\" success=\"1\"");
response(hdr, null);
}
protected void dyingThread(ContextException ex)
{
try
{
breakContext = ex.ctxt;
breakpoint = new Breakpoint(ex.ctxt.location);
status = DBGPStatus.STOPPING;
statusReason = DBGPReason.EXCEPTION;
errorResponse(DBGPErrorCode.EVALUATION_ERROR, ex.getMessage());
run();
breakContext = null;
breakpoint = null;
statusResponse(DBGPStatus.STOPPED, DBGPReason.EXCEPTION);
} catch (Exception e)
{
errorResponse(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
protected boolean processRun(DBGPCommand c) throws DBGPException
{
checkArgs(c, 1, false);
if (status == DBGPStatus.BREAK || status == DBGPStatus.STOPPING)
{
if (breakContext != null)
{
breakContext.threadState.setBreaks(null, null, null);
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
return false; // run means continue
} else
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
}
if (status == DBGPStatus.STARTING && expression == null)
{
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
return false; // a run for a new thread, means continue
}
if (status != DBGPStatus.STARTING)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
if (c.data != null) // data is in "expression"
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
if (remoteControl != null)
{
try
{
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
final RemoteInterpreter remoteInterpreter = new RemoteInterpreter(interpreter, this);
Thread remoteThread = new Thread(new Runnable()
{
public void run()
{
try
{
remoteControl.run(remoteInterpreter);
} catch (Exception e)
{
status = DBGPStatus.STOPPED;
statusReason = DBGPReason.ERROR;
errorResponse(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
});
remoteThread.setName("RemoteControl runner");
remoteThread.setDaemon(true);
remoteThread.start();
remoteInterpreter.processRemoteCalls();
// remoteControl.run(new RemoteInterpreter(interpreter, this));
stdout("Remote control completed");
statusResponse(DBGPStatus.STOPPED, DBGPReason.OK);
} catch (Exception e)
{
status = DBGPStatus.STOPPED;
statusReason = DBGPReason.ERROR;
errorResponse(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
return false; // Do not continue after remote session
} else
{
try
{
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
theAnswer = interpreter.execute(expression, this);
stdout(expression + " = " + theAnswer.toString());
statusResponse(DBGPStatus.STOPPED, DBGPReason.OK);
} catch (ContextException e)
{
dyingThread(e);
} catch (Exception e)
{
status = DBGPStatus.STOPPED;
statusReason = DBGPReason.ERROR;
errorResponse(DBGPErrorCode.EVALUATION_ERROR, e.getMessage());
}
return true;
}
}
protected boolean processEval(DBGPCommand c) throws DBGPException
{
checkArgs(c, 1, true);
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING
|| breakpoint == null)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
breaksSuspended = true;
try
{
String exp = c.data; // Already base64 decoded by the parser
interpreter.setDefaultName(breakpoint.location.getModule());
theAnswer = interpreter.evaluate(exp, breakContext);
StringBuilder property = propertyResponse(exp, exp, interpreter.getDefaultName(), theAnswer.toString());
StringBuilder hdr = new StringBuilder("success=\"1\"");
response(hdr, property);
} catch (Exception e)
{
errorResponse(DBGPErrorCode.EVALUATION_ERROR, e.getMessage());
} finally
{
breaksSuspended = false;
}
return true;
}
protected boolean processExpr(DBGPCommand c) throws DBGPException
{
checkArgs(c, 1, true);
if (status == DBGPStatus.BREAK || status == DBGPStatus.STOPPING)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
try
{
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
String exp = c.data; // Already base64 decoded by the parser
theAnswer = interpreter.execute(exp, this);
StringBuilder property = propertyResponse(exp, exp, interpreter.getDefaultName(), theAnswer.toString());
StringBuilder hdr = new StringBuilder("success=\"1\"");
status = DBGPStatus.STOPPED;
statusReason = DBGPReason.OK;
response(hdr, property);
} catch (ContextException e)
{
dyingThread(e);
} catch (Exception e)
{
status = DBGPStatus.STOPPED;
statusReason = DBGPReason.ERROR;
errorResponse(DBGPErrorCode.EVALUATION_ERROR, e.getMessage());
}
return true;
}
protected void processStepInto(DBGPCommand c) throws DBGPException
{
checkArgs(c, 1, false);
if (breakpoint != null)
{
breakContext.threadState.setBreaks(breakpoint.location, null, null);
}
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
}
protected void processStepOver(DBGPCommand c) throws DBGPException
{
checkArgs(c, 1, false);
if (breakpoint != null)
{
breakContext.threadState.setBreaks(breakpoint.location, breakContext.getRoot(), null);
}
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
}
protected void processStepOut(DBGPCommand c) throws DBGPException
{
checkArgs(c, 1, false);
if (breakpoint != null)
{
breakContext.threadState.setBreaks(breakpoint.location, null, breakContext.getRoot().outer);
}
status = DBGPStatus.RUNNING;
statusReason = DBGPReason.OK;
}
protected void processStop(DBGPCommand c) throws DBGPException, IOException
{
checkArgs(c, 1, false);
statusResponse(DBGPStatus.STOPPED, DBGPReason.OK);
if (isLastConnectedReader())
{
handleExit();
} else
{
removeThisReader();
}
}
protected void handleExit()
{
try
{
if (socket != null)
{
socket.close();
}
} catch (Exception e)
{
}
System.exit(0);
}
private boolean isLastConnectedReader()
{
synchronized (connectecReaders)
{
return connectecReaders.size() == 1;
}
}
private void addThisReader()
{
synchronized (connectecReaders)
{
connectecReaders.add(this);
}
}
private void removeThisReader()
{
synchronized (connectecReaders)
{
connectecReaders.remove(this);
}
}
protected void breakpointGet(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 2, false);
DBGPOption option = c.getOption(DBGPOptionType.D);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
Breakpoint bp = interpreter.breakpoints.get(Integer.parseInt(option.value));
if (bp == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_BREAKPOINT, c.toString());
}
response(null, breakpointResponse(bp));
}
protected void breakpointSet(DBGPCommand c) throws DBGPException,
IOException, URISyntaxException
{
DBGPOption option = c.getOption(DBGPOptionType.T);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
DBGPBreakpointType type = DBGPBreakpointType.lookup(option.value);
if (type == null)
{
throw new DBGPException(DBGPErrorCode.BREAKPOINT_TYPE_UNSUPPORTED, option.value);
}
option = c.getOption(DBGPOptionType.F);
File filename = null;
if (option != null)
{
filename = new File(new URI(option.value));
} else
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
option = c.getOption(DBGPOptionType.N);
int lineno = 0;
if (option != null)
{
lineno = Integer.parseInt(option.value);
}
String condition = null;
if (c.data != null)
{
condition = c.data;
} else
{
DBGPOption cond = c.getOption(DBGPOptionType.O);
DBGPOption hits = c.getOption(DBGPOptionType.H);
if (cond != null || hits != null)
{
String cs = cond == null ? ">=" : cond.value;
String hs = hits == null ? "0" : hits.value;
if (hs.equals("0"))
{
condition = "= 0"; // impossible (disabled)
} else if (cs.equals("=="))
{
condition = "= " + hs;
} else if (cs.equals(">="))
{
condition = ">= " + hs;
} else if (cs.equals("%"))
{
condition = "mod " + hs;
} else
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
}
}
Breakpoint bp = null;
PStm stmt = interpreter.findStatement(filename, lineno);
if (stmt == null)
{
PExp exp = interpreter.findExpression(filename, lineno);
if (exp == null)
{
throw new DBGPException(DBGPErrorCode.CANT_SET_BREAKPOINT, filename
+ ":" + lineno);
} else
{
try
{
if (BreakpointManager.getBreakpoint(exp).number != 0)
{
// Multiple threads set BPs multiple times, so...
bp = BreakpointManager.getBreakpoint(exp); // Re-use the existing one
} else
{
bp = interpreter.setBreakpoint(exp, condition);
}
} catch (ParserException e)
{
throw new DBGPException(DBGPErrorCode.CANT_SET_BREAKPOINT, filename
+ ":" + lineno + ", " + e.getMessage());
} catch (LexException e)
{
throw new DBGPException(DBGPErrorCode.CANT_SET_BREAKPOINT, filename
+ ":" + lineno + ", " + e.getMessage());
}
}
} else
{
try
{
if (BreakpointManager.getBreakpoint(stmt).number != 0)
{
// Multiple threads set BPs multiple times, so...
bp = BreakpointManager.getBreakpoint(stmt); // Re-use the existing one
} else
{
bp = interpreter.setBreakpoint(stmt, condition);
}
} catch (ParserException e)
{
throw new DBGPException(DBGPErrorCode.CANT_SET_BREAKPOINT, filename
+ ":" + lineno + ", " + e.getMessage());
} catch (LexException e)
{
throw new DBGPException(DBGPErrorCode.CANT_SET_BREAKPOINT, filename
+ ":" + lineno + ", " + e.getMessage());
}
}
option = c.getOption(DBGPOptionType.S);
if (option != null)
{
if (!option.value.equalsIgnoreCase("enabled"))
{
bp.setEnabled(false);
}
}
StringBuilder hdr = new StringBuilder("state=\""
+ (bp.isEnabled() ? "enabled" : "disabled") + "\" id=\""
+ bp.number + "\"");
response(hdr, null);
}
protected void breakpointUpdate(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 4, false);
DBGPOption option = c.getOption(DBGPOptionType.D);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
Breakpoint bp = interpreter.breakpoints.get(Integer.parseInt(option.value));
if (bp == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_BREAKPOINT, c.toString());
}
option = c.getOption(DBGPOptionType.S);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
if (option.value.equalsIgnoreCase("disabled"))
{
bp.setEnabled(false);
response(null, null);
return;
}
if (option.value.equalsIgnoreCase("enabled"))
{
bp.setEnabled(true);
response(null, null);
return;
}
throw new DBGPException(DBGPErrorCode.UNIMPLEMENTED, c.toString());
}
protected void breakpointRemove(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 2, false);
DBGPOption option = c.getOption(DBGPOptionType.D);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
if (interpreter.clearBreakpoint(Integer.parseInt(option.value)) == null)
{
// Multiple threads remove BPs multiple times
// throw new DBGPException(DBGPErrorCode.INVALID_BREAKPOINT, c.toString());
}
response(null, null);
}
protected void breakpointList(DBGPCommand c) throws IOException,
DBGPException
{
checkArgs(c, 1, false);
StringBuilder bps = new StringBuilder();
for (Integer key : interpreter.breakpoints.keySet())
{
Breakpoint bp = interpreter.breakpoints.get(key);
bps.append(breakpointResponse(bp));
}
response(null, bps);
}
protected void stackDepth(DBGPCommand c) throws DBGPException, IOException
{
checkArgs(c, 1, false);
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
StringBuilder sb = new StringBuilder();
sb.append(breakContext.getDepth());
response(null, sb);
}
protected void stackGet(DBGPCommand c) throws DBGPException, IOException
{
checkArgs(c, 1, false);
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING
|| breakpoint == null)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
DBGPOption option = c.getOption(DBGPOptionType.D);
int depth = -1;
if (option != null)
{
depth = Integer.parseInt(option.value); // 0 to n-1
}
// We omit the last frame, as this is unhelpful (globals),
int actualDepth = breakContext.getDepth() - 1;
if (depth >= actualDepth)
{
throw new DBGPException(DBGPErrorCode.INVALID_STACK_DEPTH, c.toString());
}
if (depth == 0)
{
response(null, stackResponse(breakpoint.location, 0));
} else if (depth > 0)
{
Context ctxt = breakContext.getFrame(depth);
response(null, stackResponse(ctxt.location, depth));
} else
{
// The location of a context is where it was called from, so
// to build the stack locations, we take the location of the
// level above, and the first level is the BP's location,
// assuming we have one which is different to the ctxt location.
StringBuilder sb = new StringBuilder();
int d = 0;
if (!breakpoint.location.equals(breakContext.location)) // BP is different
{
sb.append(stackResponse(breakpoint.location, d++));
actualDepth--;
}
for (int f = 0; f < actualDepth; f++)
{
Context ctxt = breakContext.getFrame(f);
sb.append(stackResponse(ctxt.location, d++));
}
response(null, sb);
}
}
protected void contextNames(DBGPCommand c) throws DBGPException,
IOException
{
if (c.data != null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
DBGPOption option = c.getOption(DBGPOptionType.D);
if (c.options.size() > (option == null ? 1 : 2))
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
StringBuilder names = new StringBuilder();
// String dialect = Settings.dialect == Dialect.VDM_SL ? "Module" : "Class";
// names.append("<context name=\"Local\" id=\"0\"/>");
// names.append("<context name=\"" + dialect + "\" id=\"1\"/>");
// names.append("<context name=\"Global\" id=\"2\"/>");
for (DBGPContextType type : DBGPContextType.values())
{
String name = type == DBGPContextType.CLASS
&& Settings.dialect == Dialect.VDM_SL ? "Module"
: type.name();
names.append("<context name=\"" + name + "\" id=\"" + type.code
+ "\"/>");
}
response(null, names);
}
protected NameValuePairMap getContextValues(DBGPContextType context,
int depth)
{
NameValuePairMap vars = new NameValuePairMap();
switch (context)
{
case LOCAL:
if (depth == 0)
{
vars.putAll(breakContext.getVisibleVariables());
} else
{
Context frame = breakContext.getFrame(depth - 1).outer;
if (frame != null)
{
vars.putAll(frame.getVisibleVariables());
}
}
if (breakContext instanceof ObjectContext)
{
ObjectContext octxt = (ObjectContext) breakContext;
int line = breakpoint.location.getStartLine();
String opname = breakContext.guardOp == null ? ""
: breakContext.guardOp.name.getName();
for (PDefinition d : octxt.self.type.getClassdef().getDefinitions())
{
if (d instanceof APerSyncDefinition)
{
APerSyncDefinition pdef = (APerSyncDefinition) d;
if (pdef.getOpname().getName().equals(opname)
|| pdef.getLocation().getStartLine() == line
|| octxt.assistantFactory.createPExpAssistant().findExpression(pdef.getGuard(), line) != null)
{
for (PExp sub : octxt.assistantFactory.createPExpAssistant().getSubExpressions(pdef.getGuard()))
{
if (sub instanceof AHistoryExp)
{
AHistoryExp hexp = (AHistoryExp) sub;
try
{
Value v = hexp.apply(VdmRuntime.getExpressionEvaluator(), octxt);
LexNameToken name = new LexNameToken(octxt.self.type.getName().getModule(), hexp.toString(), hexp.getLocation());
vars.put(name, v);
} catch (Throwable e)
{
// Ignore
}
}
}
}
} else if (d instanceof AMutexSyncDefinition)
{
AMutexSyncDefinition mdef = (AMutexSyncDefinition) d;
for (ILexNameToken mop : mdef.getOperations())
{
if (mop.getName().equals(opname))
{
for (ILexNameToken op : mdef.getOperations())
{
LexNameList ops = new LexNameList(op);// TODO: this needs to be checked when
// testing
PExp hexp = AstFactory.newAHistoryExp(mdef.getLocation(), new LexToken(new LexLocation(), VDMToken.ACTIVE), ops);
try
{
Value v = hexp.apply(VdmRuntime.getExpressionEvaluator(), octxt);
LexNameToken name = new LexNameToken(octxt.self.type.getName().getModule(), hexp.toString(), mdef.getLocation());
vars.put(name, v);
} catch (Throwable e)
{
// Ignore
}
}
break;
}
}
}
}
}
break;
case CLASS: // Includes modules
Context root = breakContext.getFrame(depth);
if (root instanceof ObjectContext)
{
ObjectContext octxt = (ObjectContext) root;
vars.putAll(octxt.self.members);
} else if (root instanceof ClassContext)
{
ClassContext cctxt = (ClassContext) root;
vars.putAll(cctxt.assistantFactory.createSClassDefinitionAssistant().getStatics(cctxt.classdef));
} else if (root instanceof StateContext)
{
StateContext sctxt = (StateContext) root;
if (sctxt.stateCtxt != null)
{
vars.putAll(sctxt.stateCtxt);
}
}
break;
case GLOBAL:
vars.putAll(interpreter.initialContext);
break;
}
return vars;
}
protected void contextGet(DBGPCommand c) throws DBGPException, IOException
{
if (c.data != null || c.options.size() > 3)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
DBGPOption option = c.getOption(DBGPOptionType.C);
int type = 0;
if (option != null)
{
type = Integer.parseInt(option.value);
}
DBGPContextType context = DBGPContextType.lookup(type);
option = c.getOption(DBGPOptionType.D);
int depth = 0;
if (option != null)
{
depth = Integer.parseInt(option.value);
}
int actualDepth = breakContext.getDepth() - 1;
if (depth >= actualDepth)
{
throw new DBGPException(DBGPErrorCode.INVALID_STACK_DEPTH, c.toString());
}
NameValuePairMap vars = getContextValues(context, depth);
response(null, propertyResponse(vars, context));
}
protected void propertyGet(DBGPCommand c) throws DBGPException, IOException
{
if (c.data != null || c.options.size() > 4)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
DBGPOption option = c.getOption(DBGPOptionType.C);
int type = 0;
if (option != null)
{
type = Integer.parseInt(option.value);
}
DBGPContextType context = DBGPContextType.lookup(type);
option = c.getOption(DBGPOptionType.D);
int depth = -1;
if (option != null)
{
depth = Integer.parseInt(option.value);
}
option = c.getOption(DBGPOptionType.N);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.CANT_GET_PROPERTY, c.toString());
}
LexTokenReader ltr = new LexTokenReader(option.value, Dialect.VDM_PP);
LexToken token = null;
try
{
token = ltr.nextToken();
} catch (LexException e)
{
throw new DBGPException(DBGPErrorCode.CANT_GET_PROPERTY, option.value);
} finally
{
ltr.close();
}
if (token.isNot(VDMToken.NAME))
{
throw new DBGPException(DBGPErrorCode.CANT_GET_PROPERTY, token.toString());
}
NameValuePairMap vars = getContextValues(context, depth);
LexNameToken longname = (LexNameToken) token;
Value value = vars.get(longname);
if (value == null)
{
throw new DBGPException(DBGPErrorCode.CANT_GET_PROPERTY, longname.toString());
}
response(null, propertyResponse(longname, value, context));
}
protected void processSource(DBGPCommand c) throws DBGPException,
IOException
{
if (c.data != null || c.options.size() > 4)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
DBGPOption option = c.getOption(DBGPOptionType.B);
int begin = 1;
if (option != null)
{
begin = Integer.parseInt(option.value);
}
option = c.getOption(DBGPOptionType.E);
int end = 0;
if (option != null)
{
end = Integer.parseInt(option.value);
}
option = c.getOption(DBGPOptionType.F);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
File file = null;
try
{
file = new File(new URI(option.value));
} catch (URISyntaxException e)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
SourceFile s = interpreter.getSourceFile(file);
StringBuilder sb = new StringBuilder();
if (end == 0)
{
end = s.getCount();
}
sb.append("<![CDATA[");
for (int n = begin; n <= end; n++)
{
sb.append(quote(s.getLine(n)));
sb.append("\n");
}
sb.append("]]>");
response(null, sb);
}
protected void processStdout(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 2, false);
DBGPOption option = c.getOption(DBGPOptionType.C);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
DBGPRedirect redirect = DBGPRedirect.lookup(option.value);
Console.directStdout(this, redirect);
response(new StringBuilder("success=\"1\""), null);
}
protected void processStderr(DBGPCommand c) throws DBGPException,
IOException
{
checkArgs(c, 2, false);
DBGPOption option = c.getOption(DBGPOptionType.C);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
DBGPRedirect redirect = DBGPRedirect.lookup(option.value);
Console.directStderr(this, redirect);
response(new StringBuilder("success=\"1\""), null);
}
public synchronized void stdout(String line) throws IOException
{
StringBuilder sb = new StringBuilder("<stream type=\"stdout\"><![CDATA[");
sb.append(Base64.encode(line.getBytes("UTF-8")));
sb.append("]]></stream>\n");
write(sb);
}
public synchronized void stderr(String line) throws IOException
{
StringBuilder sb = new StringBuilder("<stream type=\"stderr\"><![CDATA[");
sb.append(Base64.encode(line.getBytes("UTF-8")));
sb.append("]]></stream>\n");
write(sb);
}
protected void processOvertureCmd(DBGPCommand c) throws DBGPException,
IOException, URISyntaxException, AnalysisException
{
checkArgs(c, 2, false);
DBGPOption option = c.getOption(DBGPOptionType.C);
if (option == null)
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
if (option.value.equals("init"))
{
processInit(c);
} else if (option.value.equals("create"))
{
processCreate(c);
} else if (option.value.equals("currentline"))
{
processCurrentLine(c);
} else if (option.value.equals("source"))
{
processCurrentSource(c);
} else if (option.value.equals("coverage"))
{
processCoverage(c);
} else if (option.value.equals("runtrace"))
{
processRuntrace(c);
} else if (option.value.startsWith("latex"))
{
processLatex(c);
} else if (option.value.equals("word"))
{
processWord(c);
} else if (option.value.equals("pog"))
{
processPOG(c);
} else if (option.value.equals("stack"))
{
processStack(c);
} else if (option.value.equals("trace"))
{
processTrace(c);
} else if (option.value.equals("list"))
{
processList();
} else if (option.value.equals("files"))
{
processFiles();
} else if (option.value.equals("classes"))
{
processClasses();
} else if (option.value.equals("modules"))
{
processModules();
} else if (option.value.equals("default"))
{
processDefault(c);
} else if (option.value.equals("log"))
{
processLog(c);
} else
{
throw new DBGPException(DBGPErrorCode.INVALID_OPTIONS, c.toString());
}
}
protected void processInit(DBGPCommand c) throws IOException, DBGPException
{
if (status == DBGPStatus.BREAK || status == DBGPStatus.STOPPING)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
LexLocationUtils.clearLocations();
interpreter.init(this);
statusResponse(DBGPStatus.STOPPED, DBGPReason.OK);
cdataResponse("Global context and test coverage initialized");
}
protected void processLog(DBGPCommand c) throws IOException
{
StringBuilder out = new StringBuilder();
try
{
if (c.data == null)
{
if (RTLogger.getLogSize() > 0)
{
out.append("Flushing " + RTLogger.getLogSize()
+ " RT events\n");
}
RTLogger.setLogfile(RTTextLogger.class, null);
RTLogger.setLogfile(NextGenRTLogger.class, (File) null);
out.append("RT events now logged to the console");
} else if (c.data.equals("off"))
{
RTLogger.enable(false);
out.append("RT event logging disabled");
} else
{
RTLogger.setLogfile(RTTextLogger.class, new File(c.data));
out.append("RT events now logged to " + c.data);
}
} catch (FileNotFoundException e)
{
out.append("Cannot create RT event log: " + e.getMessage());
}
cdataResponse(out.toString());
}
protected void processCreate(DBGPCommand c) throws DBGPException
{
if (!(interpreter instanceof ClassInterpreter))
{
throw new DBGPException(DBGPErrorCode.INTERNAL_ERROR, "Not available for VDM-SL");
}
try
{
int i = c.data.indexOf(' ');
String var = c.data.substring(0, i);
String exp = c.data.substring(i + 1);
((ClassInterpreter) interpreter).create(var, exp);
} catch (Exception e)
{
throw new DBGPException(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
protected void processStack(DBGPCommand c) throws IOException,
DBGPException
{
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING
|| breakpoint == null)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
pw.println(breakpoint.stoppedAtString());
breakContext.printStackTrace(pw, true);
pw.close();
cdataResponse(out.toString());
}
protected void processTrace(DBGPCommand c) throws DBGPException
{
File file = null;
int line = 0;
String trace = null;
try
{
int i = c.data.indexOf(' ');
int j = c.data.indexOf(' ', i + 1);
file = new File(new URI(c.data.substring(0, i)));
line = Integer.parseInt(c.data.substring(i + 1, j));
trace = c.data.substring(j + 1);
if (trace.length() == 0)
{
trace = null;
}
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
PStm stmt = interpreter.findStatement(file, line);
if (stmt == null)
{
PExp exp = interpreter.findExpression(file, line);
if (exp == null)
{
throw new DBGPException(DBGPErrorCode.CANT_SET_BREAKPOINT, "No breakable expressions or statements at "
+ file + ":" + line);
} else
{
interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(exp).number);
Breakpoint bp = interpreter.setTracepoint(exp, trace);
pw.println("Created " + bp);
pw.println(interpreter.getSourceLine(bp.location));
}
} else
{
interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(stmt).number);
Breakpoint bp = interpreter.setTracepoint(stmt, trace);
pw.println("Created " + bp);
pw.println(interpreter.getSourceLine(bp.location));
}
pw.close();
cdataResponse(out.toString());
} catch (Exception e)
{
throw new DBGPException(DBGPErrorCode.CANT_SET_BREAKPOINT, e.getMessage());
}
}
protected void processList() throws IOException
{
Map<Integer, Breakpoint> map = interpreter.getBreakpoints();
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
for (Entry<Integer, Breakpoint> entry : map.entrySet())
{
Breakpoint bp = entry.getValue();
pw.println(bp.toString());
pw.println(interpreter.getSourceLine(bp.location));
}
pw.close();
cdataResponse(out.toString());
}
protected void processFiles() throws IOException
{
Set<File> filenames = interpreter.getSourceFiles();
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
for (File file : filenames)
{
pw.println(file.getPath());
}
pw.close();
cdataResponse(out.toString());
}
protected void processClasses() throws IOException, DBGPException
{
if (!(interpreter instanceof ClassInterpreter))
{
throw new DBGPException(DBGPErrorCode.INTERNAL_ERROR, "Not available for VDM-SL");
}
ClassInterpreter cinterpreter = (ClassInterpreter) interpreter;
String def = cinterpreter.getDefaultName();
ClassList classes = cinterpreter.getClasses();
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
for (SClassDefinition cls : classes)
{
if (cls.getName().getName().equals(def))
{
pw.println(cls.getName().getName() + " (default)");
} else
{
pw.println(cls.getName().getName());
}
}
pw.close();
cdataResponse(out.toString());
}
protected void processModules() throws DBGPException, IOException
{
if (!(interpreter instanceof ModuleInterpreter))
{
throw new DBGPException(DBGPErrorCode.INTERNAL_ERROR, "Only available for VDM-SL");
}
ModuleInterpreter minterpreter = (ModuleInterpreter) interpreter;
String def = minterpreter.getDefaultName();
List<AModuleModules> modules = minterpreter.getModules();
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
for (AModuleModules m : modules)
{
if (m.getName().getName().equals(def))
{
pw.println(m.getName().getName() + " (default)");
} else
{
pw.println(m.getName().getName());
}
}
pw.close();
cdataResponse(out.toString());
}
protected void processDefault(DBGPCommand c) throws DBGPException
{
try
{
interpreter.setDefaultName(c.data);
cdataResponse("Default set to " + interpreter.getDefaultName());
} catch (Exception e)
{
throw new DBGPException(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
protected void processCoverage(DBGPCommand c) throws DBGPException,
IOException, URISyntaxException
{
if (status == DBGPStatus.BREAK)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
File file = new File(new URI(c.data));
SourceFile source = interpreter.getSourceFile(file);
if (source == null)
{
cdataResponse(file + ": file not found");
} else
{
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
source.printCoverage(pw);
pw.close();
cdataResponse(out.toString());
}
}
protected void processRuntrace(DBGPCommand c) throws DBGPException
{
if (status == DBGPStatus.BREAK)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
try
{
String[] parts = c.data.split("\\s+");
int testNo = Integer.parseInt(parts[1]);
boolean debug = Boolean.parseBoolean(parts[2]);
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
Interpreter.setTraceOutput(pw);
interpreter.runtrace(parts[0], testNo, debug);
pw.close();
cdataResponse(out.toString());
} catch (Exception e)
{
throw new DBGPException(DBGPErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
protected void processLatex(DBGPCommand c) throws DBGPException,
IOException, URISyntaxException
{
if (status == DBGPStatus.BREAK)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
int i = c.data.indexOf(' ');
File dir = new File(new URI(c.data.substring(0, i)));
File file = new File(new URI(c.data.substring(i + 1)));
SourceFile source = interpreter.getSourceFile(file);
boolean headers = c.getOption(DBGPOptionType.C).value.equals("latexdoc");
if (source == null)
{
cdataResponse(file + ": file not found");
} else
{
File tex = new File(dir.getPath() + File.separator + file.getName()
+ ".tex");
PrintWriter pw = new PrintWriter(tex);
new LatexSourceFile(source).printCoverage(pw, headers);
pw.close();
cdataResponse("Latex coverage written to " + tex);
}
}
private void processWord(DBGPCommand c) throws DBGPException, IOException,
URISyntaxException
{
if (status == DBGPStatus.BREAK)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
int i = c.data.indexOf(' ');
File dir = new File(new URI(c.data.substring(0, i)));
File file = new File(new URI(c.data.substring(i + 1)));
SourceFile source = interpreter.getSourceFile(file);
if (source == null)
{
cdataResponse(file + ": file not found");
} else
{
File html = new File(dir.getPath() + File.separator
+ file.getName() + ".doc");
PrintWriter pw = new PrintWriter(html);
source.printWordCoverage(pw);
pw.close();
cdataResponse("Word HTML coverage written to " + html);
}
}
protected void processCurrentLine(DBGPCommand c) throws DBGPException,
IOException
{
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING
|| breakpoint == null)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
OutputStream out = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(out);
pw.println(breakpoint.stoppedAtString());
pw.println(interpreter.getSourceLine(breakpoint.location.getFile(), breakpoint.location.getStartLine(), ": "));
pw.close();
cdataResponse(out.toString());
}
protected void processCurrentSource(DBGPCommand c) throws DBGPException,
IOException
{
if (status != DBGPStatus.BREAK && status != DBGPStatus.STOPPING
|| breakpoint == null)
{
throw new DBGPException(DBGPErrorCode.NOT_AVAILABLE, c.toString());
}
File file = breakpoint.location.getFile();
int current = breakpoint.location.getStartLine();
int start = current - SOURCE_LINES;
if (start < 1)
{
start = 1;
}
int end = start + SOURCE_LINES * 2 + 1;
StringBuilder sb = new StringBuilder();
for (int src = start; src < end; src++)
{
sb.append(interpreter.getSourceLine(file, src, src == current ? ":>>"
: ": "));
sb.append("\n");
}
cdataResponse(sb.toString());
}
protected void processPOG(DBGPCommand c) throws IOException,
AnalysisException
{
IProofObligationList all = interpreter.getProofObligations();
IProofObligationList list = null;
if (c.data.equals("*"))
{
list = all;
} else
{
list = new ProofObligationList();
String name = c.data + "(";
for (IProofObligation po : all)
{
if (po.getName().indexOf(name) >= 0)
{
list.add(po);
}
}
}
if (list.isEmpty())
{
cdataResponse("No proof obligations generated");
} else
{
StringBuilder sb = new StringBuilder();
sb.append("Generated ");
sb.append(plural(list.size(), "proof obligation", "s"));
sb.append(":\n");
sb.append(list);
cdataResponse(sb.toString());
}
}
protected String plural(int n, String s, String pl)
{
return n + " " + (n != 1 ? s + pl : s);
}
protected static void writeCoverage(Interpreter interpreter, File coverage)
throws IOException
{
for (File f : interpreter.getSourceFiles())
{
SourceFile source = interpreter.getSourceFile(f);
File data = new File(coverage.getPath() + File.separator
+ f.getName() + ".cov");
PrintWriter pw = new PrintWriter(data);
source.writeCoverage(pw);
pw.close();
}
}
}