/** Evaluates the given script. */
 public String eval(String expr) {
   String result = "undefined";
   if (expr == null) {
     return result;
   }
   ContextData contextData = currentContextData();
   if (contextData == null || frameIndex >= contextData.frameCount()) {
     return result;
   }
   StackFrame frame = contextData.getFrame(frameIndex);
   if (contextData.eventThreadFlag) {
     Context cx = Context.getCurrentContext();
     result = do_eval(cx, frame, expr);
   } else {
     synchronized (monitor) {
       if (insideInterruptLoop) {
         evalRequest = expr;
         evalFrame = frame;
         monitor.notify();
         do {
           try {
             monitor.wait();
           } catch (InterruptedException exc) {
             Thread.currentThread().interrupt();
             break;
           }
         } while (evalRequest != null);
         result = evalResult;
       }
     }
   }
   return result;
 }
 /** 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;
     }
   }
 }
 /** Creates a new StackFrame. */
 private StackFrame(Context cx, Dim dim, FunctionSource fsource) {
   this.dim = dim;
   this.contextData = ContextData.get(cx);
   this.fsource = fsource;
   this.breakpoints = fsource.sourceInfo().breakpoints;
   this.lineNumber = fsource.firstLine();
 }
 /** Called when the stack frame is entered. */
 public void onEnter(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
   contextData.pushFrame(this);
   this.scope = scope;
   this.thisObj = thisObj;
   if (dim.breakOnEnter) {
     dim.handleBreakpointHit(this, cx);
   }
 }
    /** Called when the current position has changed. */
    public void onLineChange(Context cx, int lineno) {
      this.lineNumber = lineno;

      if (!breakpoints[lineno] && !dim.breakFlag) {
        boolean lineBreak = contextData.breakNextLine;
        if (lineBreak && contextData.stopAtFrameDepth >= 0) {
          lineBreak = (contextData.frameCount() <= contextData.stopAtFrameDepth);
        }
        if (!lineBreak) {
          return;
        }
        contextData.stopAtFrameDepth = -1;
        contextData.breakNextLine = false;
      }

      dim.handleBreakpointHit(this, cx);
    }
  /** 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();
      }
    }
  }
 /** Called when the stack frame has been left. */
 public void onExit(Context cx, boolean byThrow, Object resultOrException) {
   if (dim.breakOnReturn && !byThrow) {
     dim.handleBreakpointHit(this, cx);
   }
   contextData.popFrame();
 }