/** Called when a script exception has been thrown. */
 private void handleExceptionThrown(Context cx, Throwable ex, StackFrame frame) {
   if (breakOnExceptions) {
     ContextData cd = frame.contextData();
     if (cd.lastProcessedException != ex) {
       interrupted(cx, frame, ex);
       cd.lastProcessedException = ex;
     }
   }
 }
  /** Interrupts script execution. */
  private void interrupted(Context cx, final StackFrame frame, Throwable scriptException) {
    ContextData contextData = frame.contextData();
    boolean eventThreadFlag = callback.isGuiEventThread();
    contextData.eventThreadFlag = eventThreadFlag;

    boolean recursiveEventThreadCall = false;

    interruptedCheck:
    synchronized (eventThreadMonitor) {
      if (eventThreadFlag) {
        if (interruptedContextData != null) {
          recursiveEventThreadCall = true;
          break interruptedCheck;
        }
      } else {
        while (interruptedContextData != null) {
          try {
            eventThreadMonitor.wait();
          } catch (InterruptedException exc) {
            return;
          }
        }
      }
      interruptedContextData = contextData;
    }

    if (recursiveEventThreadCall) {
      // XXX: For now the following is commented out as on Linux
      // too deep recursion of dispatchNextGuiEvent causes GUI lockout.
      // Note: it can make GUI unresponsive if long-running script
      // will be called on GUI thread while processing another interrupt
      if (false) {
        // Run event dispatch until gui sets a flag to exit the initial
        // call to interrupted.
        while (this.returnValue == -1) {
          try {
            callback.dispatchNextGuiEvent();
          } catch (InterruptedException exc) {
          }
        }
      }
      return;
    }

    if (interruptedContextData == null) Kit.codeBug();

    try {
      do {
        int frameCount = contextData.frameCount();
        this.frameIndex = frameCount - 1;

        final String threadTitle = Thread.currentThread().toString();
        final String alertMessage;
        if (scriptException == null) {
          alertMessage = null;
        } else {
          alertMessage = scriptException.toString();
        }

        int returnValue = -1;
        if (!eventThreadFlag) {
          synchronized (monitor) {
            if (insideInterruptLoop) Kit.codeBug();
            this.insideInterruptLoop = true;
            this.evalRequest = null;
            this.returnValue = -1;
            callback.enterInterrupt(frame, threadTitle, alertMessage);
            try {
              for (; ; ) {
                try {
                  monitor.wait();
                } catch (InterruptedException exc) {
                  Thread.currentThread().interrupt();
                  break;
                }
                if (evalRequest != null) {
                  this.evalResult = null;
                  try {
                    evalResult = do_eval(cx, evalFrame, evalRequest);
                  } finally {
                    evalRequest = null;
                    evalFrame = null;
                    monitor.notify();
                  }
                  continue;
                }
                if (this.returnValue != -1) {
                  returnValue = this.returnValue;
                  break;
                }
              }
            } finally {
              insideInterruptLoop = false;
            }
          }
        } else {
          this.returnValue = -1;
          callback.enterInterrupt(frame, threadTitle, alertMessage);
          while (this.returnValue == -1) {
            try {
              callback.dispatchNextGuiEvent();
            } catch (InterruptedException exc) {
            }
          }
          returnValue = this.returnValue;
        }
        switch (returnValue) {
          case STEP_OVER:
            contextData.breakNextLine = true;
            contextData.stopAtFrameDepth = contextData.frameCount();
            break;
          case STEP_INTO:
            contextData.breakNextLine = true;
            contextData.stopAtFrameDepth = -1;
            break;
          case STEP_OUT:
            if (contextData.frameCount() > 1) {
              contextData.breakNextLine = true;
              contextData.stopAtFrameDepth = contextData.frameCount() - 1;
            }
            break;
        }
      } while (false);
    } finally {
      synchronized (eventThreadMonitor) {
        interruptedContextData = null;
        eventThreadMonitor.notifyAll();
      }
    }
  }