Package org.rascalmpl.interpreter.debug

Source Code of org.rascalmpl.interpreter.debug.DebugHandler

/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:

*   * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
*   * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl
*   * Emilie Balland - (CWI)
*   * Mark Hills - Mark.Hills@cwi.nl (CWI)
*   * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*   * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.interpreter.debug;

import static org.rascalmpl.interpreter.AbstractInterpreterEventTrigger.newNullEventTrigger;

import java.util.Set;

import org.eclipse.imp.pdb.facts.ISourceLocation;
import org.eclipse.swt.widgets.Display;
import org.rascalmpl.ast.AbstractAST;
import org.rascalmpl.interpreter.AbstractInterpreterEventTrigger;
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.IEvaluator;
import org.rascalmpl.interpreter.debug.IDebugMessage.Detail;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.load.RascalURIResolver;

public final class DebugHandler implements IDebugHandler {

  private AbstractInterpreterEventTrigger eventTrigger;

  private final Set<String> breakpoints = new java.util.HashSet<String>();
     
  /**
   * Indicates a manual suspend request from the debugger, e.g. caused by a pause action in the GUI.
   */
  private boolean suspendRequested;

  /**
   * Indicates that the evaluator is suspended. Also used for suspending / blocking the evaluator.
   */
  private boolean suspended;
 
  private enum DebugStepMode {
    NO_STEP, STEP_INTO, STEP_OVER
  }
 
  private DebugStepMode stepMode = DebugStepMode.NO_STEP;
 
  /**
   * Referring to {@link AbstractAST} responsible for last suspension.
   */
  private AbstractAST referenceAST = null

  /**
   * Referring to the {@link Environment} stack depth at last suspension suspension.
   * This information is used to determine if stepping enters a function call.
   * {@see #suspend(IEvaluator, AbstractAST)}
   */ 
  private Integer referenceEnvironmentStackSize = null;

  /**
   * Action to execute on termination request, or <code>null</code> if none.
   */
  private Runnable terminateAction = null;

  /**
   * To resolve rascal:/// source locs
   */
  private final RascalURIResolver resolver;
 

  /**
   * Create a new debug handler with its own interpreter event trigger.
   */
  public DebugHandler(RascalURIResolver resolver) {
    this.resolver = resolver;
    setEventTrigger(newNullEventTrigger());
  }
 
  private boolean hasBreakpoint(ISourceLocation b) {
    return breakpoints.contains(resolver.resolve(b).toString());
  }
 
  private void addBreakpoint(ISourceLocation breakpointLocation) {
    breakpoints.add(breakpointLocation.toString());
  }

  private void removeBreakpoint(ISourceLocation breakpointLocation) {
    breakpoints.remove(breakpointLocation.toString());
  }
 
  protected void clearSuspensionState() {
    setReferenceAST(null);
    setReferenceEnvironmentStackSize(null);
    setSuspended(false);
 
 
  protected void updateSuspensionState(IEvaluator<?> evaluator, AbstractAST currentAST) {
    setReferenceAST(currentAST);
   
    // TODO: remove cast to {@link Evaluator} and rework {@link IEvaluator}.
    setReferenceEnvironmentStackSize(((Evaluator) evaluator).getCallStack().size());
    setSuspended(true);
  }
 
  @Override
  public void suspended(IEvaluator<?> evaluator, AbstractAST currentAST) {
    if (Display.getDefault().getThread().equals(Thread.currentThread())) {
      return;
    }
    if(isSuspendRequested()) {
      updateSuspensionState(evaluator, currentAST);
      getEventTrigger().fireSuspendByClientRequestEvent();     
   
      setSuspendRequested(false);
     
    } else {

      ISourceLocation location = currentAST.getLocation();
      switch (getStepMode()) {
     
      case STEP_INTO:
        updateSuspensionState(evaluator, currentAST);
        getEventTrigger().fireSuspendByStepEndEvent();
        break;
       
      case STEP_OVER:
        // TODO: remove cast to {@link Evaluator} and rework {@link IEvaluator}.
        // TODO: optimize {@link Evaluator.getCallStack()}; currently it is expensive because of environment traversal
        Integer currentEnvironmentStackSize = ((Evaluator) evaluator).getCallStack().size();

        /*
         * Stepping over implies:
         * * either there is a next statement in the same environment stack frame (which might
         *   equal the reference statement in case of recursion or single statement loops)
         * * or there is no next statement in the same stack frame and thus the stack frame
         *   eventually gets popped from the stack. As long the calls in deeper nesting levels
         *   are executed, no action needs to be taken.
         */
        switch (currentEnvironmentStackSize.compareTo(getReferenceEnvironmentStackSize())) {
        case 0:
          /*
           * For the case that we are still within the same stack
           * frame, positions are compared to ensure that the
           * statement was finished executing.
           */
          int referenceStart = getReferenceAST().getLocation().getOffset();
          int referenceAfter = getReferenceAST().getLocation().getOffset() + getReferenceAST().getLocation().getLength();
          int currentStart = location.getOffset();
          int currentAfter = location.getOffset() + location.getLength();

          if (currentStart < referenceStart
              || currentStart >= referenceAfter
              || currentStart == referenceStart
              && currentAfter == referenceAfter) {
            updateSuspensionState(evaluator, currentAST);
            getEventTrigger().fireSuspendByStepEndEvent();
          }
          break;

        case -1:
          // lower stack size: left scope, thus over
          updateSuspensionState(evaluator, currentAST);
          getEventTrigger().fireSuspendByStepEndEvent();
          break;

        case +1:
          // higher stack size: not over yet
          break;

        default:
          throw new RuntimeException(
              "Requires compareTo() to return exactly either -1, 0, or +1.");
        }
        break;

      case NO_STEP:
        if (hasBreakpoint(location)) {
          updateSuspensionState(evaluator, currentAST);
          location = evaluator.getRascalResolver().resolve(location);
          getEventTrigger().fireSuspendByBreakpointEvent(location);
        }
        break;

      }
    }

    /*
     * Waiting until GUI triggers end of suspension.
     */
    while (isSuspended()) {
      try {
        evaluator.wait(50);
      } catch (InterruptedException e) {
        // Ignore
      }
    }   

  }

  protected AbstractAST getReferenceAST() {
    return referenceAST;
  }

  protected void setReferenceAST(AbstractAST referenceAST) {
    this.referenceAST = referenceAST;
  }

  protected Integer getReferenceEnvironmentStackSize() {
    return referenceEnvironmentStackSize;
  }

  protected void setReferenceEnvironmentStackSize(Integer referenceEnvironmentStackSize) {
    this.referenceEnvironmentStackSize = referenceEnvironmentStackSize;
  }

  protected boolean isSuspendRequested() {
    return suspendRequested;
  }

  protected void setSuspendRequested(boolean suspendRequested) {
    this.suspendRequested = suspendRequested;
  }

  @SuppressWarnings("incomplete-switch")
  @Override
  public void processMessage(IDebugMessage message) {
    switch (message.getSubject()) {

    case BREAKPOINT:
      ISourceLocation breakpointLocation = (ISourceLocation) message.getPayload();

      switch (message.getAction()) {
      case SET:
        addBreakpoint(breakpointLocation);
        break;

      case DELETE:
        removeBreakpoint(breakpointLocation);
        break;
      }
      break;

    case INTERPRETER: 
      switch (message.getAction()) {
      case SUSPEND:
        if (message.getDetail() == Detail.CLIENT_REQUEST) {
          setSuspendRequested(true);
        }
        break;

      case RESUME:
        setSuspended(false);

        switch (message.getDetail()) {
        case STEP_INTO:
          setStepMode(DebugStepMode.STEP_INTO);
          getEventTrigger().fireResumeByStepIntoEvent();
          break;

        case STEP_OVER:
          setStepMode(DebugStepMode.STEP_OVER);
          getEventTrigger().fireResumeByStepOverEvent();
          break;

        case CLIENT_REQUEST:
          setStepMode(DebugStepMode.NO_STEP);
          getEventTrigger().fireResumeByClientRequestEvent();
          break;
        }
        break;

      case TERMINATE:
        if (terminateAction != null) {
          terminateAction.run();
        }
        break;
      }
      break;
    }
  }

  public void setTerminateAction(Runnable terminateAction) {
    this.terminateAction = terminateAction;
  }

  protected synchronized boolean isSuspended() {
    return suspended;
  }

  protected synchronized void setSuspended(boolean suspended) {
    this.suspended = suspended;
  }

  protected DebugStepMode getStepMode() {
    return stepMode;
  }

  protected void setStepMode(DebugStepMode stepMode) {
    this.stepMode = stepMode;
  }

  public AbstractInterpreterEventTrigger getEventTrigger() {
    return eventTrigger;
  }

  public void setEventTrigger(AbstractInterpreterEventTrigger eventTrigger) {
    this.eventTrigger = eventTrigger;
 

}
TOP

Related Classes of org.rascalmpl.interpreter.debug.DebugHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.