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