/******************************************************************************* * * 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.Serializable; import org.overture.ast.expressions.PExp; import org.overture.ast.intf.lex.ILexLocation; import org.overture.ast.lex.Dialect; import org.overture.ast.lex.LexIntegerToken; import org.overture.ast.lex.LexToken; import org.overture.ast.lex.VDMToken; import org.overture.config.Settings; import org.overture.interpreter.ast.expressions.BreakpointExpression; import org.overture.interpreter.messages.Console; import org.overture.interpreter.scheduler.BasicSchedulableThread; import org.overture.parser.lex.LexException; import org.overture.parser.lex.LexTokenReader; import org.overture.parser.syntax.ExpressionReader; import org.overture.parser.syntax.ParserException; /** * The root of the breakpoint class hierarchy. */ public class Breakpoint implements Serializable { private static final long serialVersionUID = 1L; /** The location of the breakpoint. */ public final ILexLocation location; /** The number of the breakpoint. */ public final int number; /** The condition or trace expression, in parsed form. */ public final PExp parsed; /** The condition or trace expression, in raw form. */ public final String trace; /** The number of times this breakpoint has been reached. */ public long hits = 0; /** The condition saying if a the breakpoint is enabled */ protected boolean enabled = true; public Breakpoint(ILexLocation location) { this.location = location; this.number = 0; this.trace = null; this.parsed = null; } /** * Create a breakpoint at the given location. The trace string is parsed to an Expression structure for subsequent * evaluation. * * @param location * The location of the breakpoint. * @param number * The number that appears in the "list" command. * @param trace * Any condition or trace expression. * @throws ParserException * @throws LexException */ public Breakpoint(ILexLocation location, int number, String trace) throws ParserException, LexException { this.location = location; this.number = number; this.trace = trace; if (trace != null) { LexTokenReader ltr = new LexTokenReader(trace, Settings.dialect); ltr.push(); LexToken tok = ltr.nextToken(); switch (tok.type) { case EQUALS: parsed = readHitCondition(ltr, BreakpointCondition.EQ); break; case GT: parsed = readHitCondition(ltr, BreakpointCondition.GT); break; case GE: parsed = readHitCondition(ltr, BreakpointCondition.GE); break; case MOD: parsed = readHitCondition(ltr, BreakpointCondition.MOD); break; default: ltr.pop(); ExpressionReader reader = new ExpressionReader(ltr); reader.setCurrentModule(location.getModule()); parsed = reader.readExpression(); break; } } else { parsed = null; } } private PExp readHitCondition(LexTokenReader ltr, BreakpointCondition cond) throws ParserException, LexException { LexToken arg = ltr.nextToken(); if (arg.isNot(VDMToken.NUMBER)) { throw new ParserException(2279, "Invalid breakpoint hit condition", location, 0); } LexIntegerToken num = (LexIntegerToken) arg; return new BreakpointExpression(this, cond, num.value); } @Override public String toString() { return location.toString(); } public String stoppedAtString() { return "Stopped [" + BasicSchedulableThread.getThreadName(Thread.currentThread()) + "] " + location; } public void clearHits() { hits = 0; } /** * Check whether to stop. The implementation in Breakpoint is used to check for the "step" and "next" commands, * using the stepline, nextctxt and outctxt fields. If the current line is different to the last step line, and the * current context is not "above" the next context or the current context equals the out context or neither the next * or out context are set, a {@link Stoppoint} is created and its check method is called - which starts a * DebuggerReader session. * * @param execl * The execution location. * @param ctxt * The execution context. */ public void check(ILexLocation execl, Context ctxt) { // skips if breakpoint is disabled // if(!enabled){ // return; // } location.hit(); hits++; ThreadState state = ctxt.threadState; if (Settings.dialect != Dialect.VDM_SL) { state.reschedule(ctxt, execl); } if (state.stepline != null) { if (execl.getStartLine() != state.stepline.getStartLine()) // NB just line, not pos { if (state.nextctxt == null && state.outctxt == null || state.nextctxt != null && !isAboveNext(ctxt.getRoot()) || state.outctxt != null && isOutOrBelow(ctxt)) { try { new Stoppoint(location, 0, null).check(location, ctxt); } catch (DebuggerException e) { throw e; } catch (Exception e) { // This happens when the Stoppoint throws an error, which // can't happen. But we need a catch clause for it anyway. throw new DebuggerException("Breakpoint [" + number + "]: " + e.getMessage()); } } } } } /** * True, if the context passed is above nextctxt. That means that the current context must have an "outer" chain * that reaches nextctxt. * * @param current * The context to test. * @return True if the current context is above nextctxt. */ private boolean isAboveNext(Context current) { Context c = current.outer; while (c != null) { if (c == current.threadState.nextctxt) { return true; } c = c.outer; } return false; } /** * True, if the context passed is equal to or below outctxt. That means that outctxt must have an "outer" chain that * reaches current context. * * @param current * The context to test. * @return True if the current context is at or below outctxt. */ private boolean isOutOrBelow(Context current) { Context c = current.threadState.outctxt; while (c != null) { if (c == current) { return true; } c = c.outer; } return false; } protected void print(String line) { Console.out.print(line); Console.out.flush(); } protected void println(String line) { Console.out.println(line); } public void setEnabled(boolean bool) { this.enabled = bool; } public boolean isEnabled() { return this.enabled; } }