/** * <copyright> * </copyright> * * */ package robot.resource.robot.debug; /** * A DebuggableInterpreter is a facade for interpreters that adds debug support. * * @param <ResultType> the result type of the actual interpreter * @param <ContextType> the context type of the actual interpreter */ public class RobotDebuggableInterpreter<ResultType, ContextType> extends robot.resource.robot.debug.AbstractRobotDebuggable { /** * The actual interpreter. Interpretation is delegated to this object. */ private robot.resource.robot.util.AbstractRobotInterpreter<ResultType, ContextType> interpreterDelegate; /** * To check whether we must stop the execution after step over/into/return, we use * a closure */ private robot.resource.robot.IRobotCommand<org.eclipse.emf.ecore.EObject> stopCondition; /** * The port of the socket that is used to send debug events to the Eclipse * debugging framework */ private int eventPort; /** * This map is used to remember the IDs of stack frame elements */ java.util.Map<Integer, org.eclipse.emf.ecore.EObject> stackFrameMap = new java.util.LinkedHashMap<Integer, org.eclipse.emf.ecore.EObject>(); /** * The ID of the last stack frame element */ int stackFrameID = 0; public RobotDebuggableInterpreter(robot.resource.robot.util.AbstractRobotInterpreter<ResultType, ContextType> interpreterDelegate, int eventPort) { this.eventPort = eventPort; this.interpreterDelegate = interpreterDelegate; this.interpreterDelegate.addListener(new robot.resource.robot.IRobotInterpreterListener() { public void handleInterpreteObject(org.eclipse.emf.ecore.EObject element) { // check whether we have hit an element after a step over/into/return evaluateStep(element); // if we are stepping we do ignore breakpoints if (stopCondition != null) { return; } // check whether we have hit a line breakpoint int line = getLine(element); org.eclipse.emf.ecore.EObject parent = element.eContainer(); if (parent != null) { int parentLine = getLine(parent); if (line == parentLine) { return; } } if (line >= 0) { evaluateLineBreakpoint(element.eResource().getURI(), line); } } }); } private ResultType interprete(ContextType context) { startUpAndWait(); // start interpretation when the debugger has sent the resume signal ResultType result = interpreterDelegate.interprete(context); terminate(); return result; } public ResultType interprete(ContextType context, boolean debugMode) { setDebugMode(debugMode); startEventSocket(eventPort); ResultType result = interprete(context); return result; } public String[] getStack() { org.eclipse.emf.ecore.EObject next = interpreterDelegate.getNextObjectToInterprete(); java.util.List<org.eclipse.emf.ecore.EObject> parents = new java.util.ArrayList<org.eclipse.emf.ecore.EObject>(); org.eclipse.emf.ecore.EObject current = next; while (current != null) { parents.add(current); current = current.eContainer(); } String[] stack = new String[parents.size()]; int i = parents.size(); for (org.eclipse.emf.ecore.EObject parent : parents) { String serializedStackElement = robot.resource.robot.util.RobotStringUtil.encode(',', new String[] {parent.eClass().getName(), Integer.toString(stackFrameID), parent.eResource().getURI().toString(), Integer.toString(getLine(parent)), Integer.toString(getCharStart(parent)), Integer.toString(getCharEnd(parent))}); stack[--i] = serializedStackElement; stackFrameMap.put(stackFrameID++, parent); } return stack; } public robot.resource.robot.util.AbstractRobotInterpreter<ResultType, ContextType> getInterpreterDelegate() { return interpreterDelegate; } private int getLine(org.eclipse.emf.ecore.EObject element) { int line = -1; robot.resource.robot.IRobotLocationMap locationMap = getLocationMap(element); if (locationMap != null) { line = locationMap.getLine(element); } return line; } private int getCharStart(org.eclipse.emf.ecore.EObject element) { robot.resource.robot.IRobotLocationMap locationMap = getLocationMap(element); if (locationMap != null) { return locationMap.getCharStart(element); } return -1; } private int getCharEnd(org.eclipse.emf.ecore.EObject element) { robot.resource.robot.IRobotLocationMap locationMap = getLocationMap(element); if (locationMap != null) { return locationMap.getCharEnd(element) + 1; } return -1; } private robot.resource.robot.IRobotLocationMap getLocationMap(org.eclipse.emf.ecore.EObject element) { org.eclipse.emf.ecore.resource.Resource resource = element.eResource(); if (resource instanceof robot.resource.robot.IRobotTextResource) { robot.resource.robot.IRobotTextResource textResource = (robot.resource.robot.IRobotTextResource) resource; robot.resource.robot.IRobotLocationMap locationMap = textResource.getLocationMap(); return locationMap; } return null; } private void evaluateStep(org.eclipse.emf.ecore.EObject element) { // create local copy to avoid race conditions robot.resource.robot.IRobotCommand<org.eclipse.emf.ecore.EObject> stopCheck = stopCondition; if (stopCheck != null && stopCheck.execute(element)) { stopCondition = null; // suspending after step... setSuspend(true); sendEvent(robot.resource.robot.debug.ERobotDebugMessageTypes.SUSPENDED, true); return; } waitIfSuspended(); } public void terminate() { interpreterDelegate.terminate(); super.terminate(); } public void stepOver() { final org.eclipse.emf.ecore.EObject current = interpreterDelegate.getNextObjectToInterprete(); final int currentLevel = robot.resource.robot.util.RobotEObjectUtil.getDepth(current); stopCondition = new robot.resource.robot.IRobotCommand<org.eclipse.emf.ecore.EObject>() { public boolean execute(org.eclipse.emf.ecore.EObject element) { // For step over, we stop at the next object that is at the same level or higher int depth = robot.resource.robot.util.RobotEObjectUtil.getDepth(element); boolean sameOrHigher = depth <= currentLevel; boolean differentElement = element != current; return sameOrHigher && differentElement; } }; resume(); } public void stepInto() { stopCondition = new robot.resource.robot.IRobotCommand<org.eclipse.emf.ecore.EObject>() { public boolean execute(org.eclipse.emf.ecore.EObject element) { // For step into, we stop at the next object return true; } }; resume(); } public void stepReturn() { org.eclipse.emf.ecore.EObject current = interpreterDelegate.getNextObjectToInterprete(); final int parentLevel = robot.resource.robot.util.RobotEObjectUtil.getDepth(current) - 1; stopCondition = new robot.resource.robot.IRobotCommand<org.eclipse.emf.ecore.EObject>() { public boolean execute(org.eclipse.emf.ecore.EObject element) { // For step return, we stop at the next object that is at least one level higher int depth = robot.resource.robot.util.RobotEObjectUtil.getDepth(element); return depth <= parentLevel; } }; resume(); } public java.util.Map<String, Object> getFrameVariables(String stackFrame) { int stackFrameID = Integer.parseInt(stackFrame); java.util.Map<String, Object> frameVariables = new java.util.LinkedHashMap<String, Object>(); frameVariables.put("this", stackFrameMap.get(stackFrameID)); frameVariables.put("context", getInterpreterDelegate().getCurrentContext()); return frameVariables; } }