/** 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(); } } }