669 lines
22 KiB
Java
669 lines
22 KiB
Java
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* 'Model' that manages most interaction with debug API
|
|
*/
|
|
|
|
// when who what
|
|
// 06/27/97 jband added this header to my code
|
|
//
|
|
|
|
package com.netscape.jsdebugging.ifcui;
|
|
|
|
import java.util.Observable;
|
|
import java.util.Observer;
|
|
import netscape.application.*;
|
|
import netscape.util.*;
|
|
import com.netscape.jsdebugging.ifcui.palomar.util.*;
|
|
import netscape.security.PrivilegeManager;
|
|
import netscape.security.ForbiddenTargetException;
|
|
import com.netscape.jsdebugging.api.*;
|
|
|
|
public class ControlTyrant
|
|
extends Observable
|
|
implements Observer, Target, JSErrorReporter
|
|
{
|
|
public static final int RUNNING = 0;
|
|
public static final int STOPPED = 1;
|
|
|
|
// ctors
|
|
|
|
public ControlTyrant(Emperor emperor) throws ForbiddenTargetException
|
|
{
|
|
super();
|
|
_app = Application.application();
|
|
_emperor = emperor;
|
|
|
|
PrivilegeManager.enablePrivilege("Debugger");
|
|
_dc = _emperor.getDebugController();
|
|
|
|
_semaphore = new CtrlSemaphore();
|
|
|
|
// set our interrupt hook (with chaining)
|
|
_interruptHook = new CtrlInterruptHook(this);
|
|
_interruptHook.setNextHook(_dc.setInterruptHook(_interruptHook));
|
|
|
|
// set our debugBreak hook (with chaining)
|
|
_debugBreakHook = new CtrlDebugBreakHook(this);
|
|
_debugBreakHook.setNextHook(_dc.setDebugBreakHook(_debugBreakHook));
|
|
|
|
_dc.setErrorReporter(this);
|
|
|
|
if( _emperor.getHostMode() == _emperor.REMOTE_SERVER )
|
|
_useServerSideStepper = true;
|
|
else
|
|
_useServerSideStepper = false;
|
|
|
|
if(AS.DEBUG)
|
|
{
|
|
_uiThreadForAssertCheck = Thread.currentThread();
|
|
}
|
|
|
|
// add ourself as observer of ???
|
|
|
|
// notify that we are running (if anyone cares at this point!)
|
|
_state = RUNNING;
|
|
_notifyOfStateChange();
|
|
}
|
|
|
|
// accessors
|
|
|
|
public int getState() {return _state;}
|
|
public boolean getInterrupt() {return _interrupt;}
|
|
public boolean getEnabled() {return _enabled;}
|
|
public Emperor getEmperor() {return _emperor;}
|
|
|
|
public JSThreadState getThreadState()
|
|
{
|
|
if( STOPPED == _state )
|
|
return _threadState;
|
|
if(AS.S)ER.T(false,"getThreadState called when not really stopped",this);
|
|
return null;
|
|
}
|
|
|
|
public JSPC getPC()
|
|
{
|
|
if( STOPPED == _state )
|
|
return _pc;
|
|
if(AS.S)ER.T(false,"getPC called when not really stopped",this);
|
|
return null;
|
|
}
|
|
|
|
public JSSourceLocation getSourceLocation()
|
|
{
|
|
if( STOPPED == _state )
|
|
return _sourceLocation;
|
|
if(AS.S)ER.T(false,"getSourceLocation called when not really stopped",this);
|
|
return null;
|
|
}
|
|
|
|
// command handlers
|
|
|
|
public synchronized void interrupt(boolean b)
|
|
{
|
|
PrivilegeManager.enablePrivilege("Debugger");
|
|
if( b == _interrupt )
|
|
return;
|
|
_interrupt = b;
|
|
if( _interrupt )
|
|
{
|
|
_stepHandler = null;
|
|
_serverSideStepperIsSet = false;
|
|
_dc.sendInterrupt();
|
|
}
|
|
}
|
|
|
|
public synchronized void runit()
|
|
{
|
|
_continueAndNotify(true);
|
|
}
|
|
|
|
public synchronized void abort()
|
|
{
|
|
if( _state != STOPPED || _semaphore.available() || null == _threadState )
|
|
{
|
|
if(AS.S)ER.T(false,"abort called when not really stopped",this);
|
|
return;
|
|
}
|
|
|
|
_threadState.setContinueState( ThreadStateBase.DEBUG_STATE_THROW );
|
|
_continueAndNotify(true);
|
|
}
|
|
|
|
private static final int STEP_OVER = 0;
|
|
private static final int STEP_INTO = 1;
|
|
private static final int STEP_OUT = 2;
|
|
|
|
public synchronized void stepOver()
|
|
{
|
|
if( _useServerSideStepper )
|
|
_setServerSideStepper(STEP_OVER);
|
|
else
|
|
_setStepHandler( new StepOver(_threadState, _sourceLocation, _pc) );
|
|
}
|
|
|
|
public synchronized void stepInto()
|
|
{
|
|
if( _useServerSideStepper )
|
|
_setServerSideStepper(STEP_INTO);
|
|
else
|
|
_setStepHandler( new StepInto(_sourceLocation, _pc) );
|
|
}
|
|
|
|
public synchronized void stepOut()
|
|
{
|
|
if( _useServerSideStepper )
|
|
_setServerSideStepper(STEP_OUT);
|
|
else
|
|
_setStepHandler( new StepOut(_threadState, _pc) );
|
|
}
|
|
|
|
private synchronized void _setServerSideStepper( int type )
|
|
{
|
|
if( _state != STOPPED || _semaphore.available() )
|
|
{
|
|
if(AS.S)ER.T(false,"_setServerSideStepper called when not really stopped",this);
|
|
return;
|
|
}
|
|
_interrupt = false;
|
|
_stepHandler = null;
|
|
PrivilegeManager.enablePrivilege("Debugger");
|
|
switch(type)
|
|
{
|
|
case STEP_OVER:
|
|
_dc.sendInterruptStepOver(_threadState);
|
|
break;
|
|
case STEP_INTO:
|
|
_dc.sendInterruptStepInto(_threadState);
|
|
break;
|
|
case STEP_OUT :
|
|
_dc.sendInterruptStepOut(_threadState);
|
|
break;
|
|
default:
|
|
if(AS.S)ER.T(false,"invalid type passed to _setServerSideStepper",this);
|
|
return;
|
|
}
|
|
_serverSideStepperIsSet = true;
|
|
_serverSideStepperType = type;
|
|
|
|
_continueAndNotify(true);
|
|
}
|
|
|
|
private synchronized void _setStepHandler( StepHandler handler )
|
|
{
|
|
if( _state != STOPPED || _semaphore.available() )
|
|
{
|
|
if(AS.S)ER.T(false,"_setStepHandler called when not really stopped",this);
|
|
return;
|
|
}
|
|
_interrupt = false;
|
|
_stepHandler = handler;
|
|
_serverSideStepperIsSet = false;
|
|
PrivilegeManager.enablePrivilege("Debugger");
|
|
_dc.sendInterrupt();
|
|
_continueAndNotify(true);
|
|
}
|
|
|
|
public synchronized void disableDebugger()
|
|
{
|
|
if( false == _enabled )
|
|
return;
|
|
|
|
_enabled = false;
|
|
|
|
if( _state == STOPPED && ! _semaphore.available() )
|
|
{
|
|
_continueAndNotify(false);
|
|
}
|
|
|
|
_notifyOfDebuggerDisabled();
|
|
}
|
|
|
|
private void _continueAndNotify( boolean notify )
|
|
{
|
|
if( _state != STOPPED || _semaphore.available() )
|
|
{
|
|
if(AS.S)ER.T(false,"_continueAndNotify called when not really stopped",this);
|
|
return;
|
|
}
|
|
|
|
// if(AS.DEBUG){System.out.println( "running again" );}
|
|
|
|
// transition state
|
|
_state = RUNNING;
|
|
if( notify )
|
|
_notifyOfStateChange();
|
|
|
|
synchronized(_semaphore)
|
|
{
|
|
_semaphore.release();
|
|
_threadState.resume();
|
|
// if(AS.DEBUG){System.out.println( "returning after interrupt" );}
|
|
}
|
|
}
|
|
|
|
public synchronized void evaluatingBreakpoint(boolean b)
|
|
{
|
|
_evaluatingBreakpoint += (b ? 1 : -1);
|
|
if(AS.S)ER.T(_evaluatingBreakpoint >= 0,"_evaluatingBreakpoint less than zero",this);
|
|
}
|
|
|
|
public synchronized void breakpointHookCalledButElectedNotToStop(JSThreadState ts)
|
|
{
|
|
if( _useServerSideStepper )
|
|
{
|
|
if(_serverSideStepperIsSet )
|
|
_dc.reinstateStepper(ts);
|
|
}
|
|
else
|
|
{
|
|
if( null != _stepHandler )
|
|
{
|
|
PrivilegeManager.enablePrivilege("Debugger");
|
|
_dc.sendInterrupt();
|
|
}
|
|
}
|
|
}
|
|
|
|
// implement our hooks
|
|
|
|
// called by both breakpoints and interrupt hook (on JS thread)
|
|
void aboutToExecute(JSThreadState debug, JSPC pc, Hook hook)
|
|
{
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "1) entered" );
|
|
if( ! _enabled )
|
|
{
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "2) exit, ! _enabled" );
|
|
return;
|
|
}
|
|
|
|
if( _evaluatingBreakpoint > 0 )
|
|
{
|
|
if(AS.DEBUG)System.out.println("ignoring break while evaluating breakpoint");
|
|
return;
|
|
}
|
|
|
|
PrivilegeManager.enablePrivilege("Debugger");
|
|
|
|
// grab the semaphore (return if not available)
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "3) about to call _semaphore.grab()" );
|
|
if( ! _semaphore.grab() )
|
|
{
|
|
if(AS.DEBUG)System.out.println( "blowing past nested break at: " + pc );
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "4) exit _semaphore.grab() failed" );
|
|
return;
|
|
}
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "5) exit _semaphore.grab() succeeded" );
|
|
|
|
if( hook instanceof CtrlDebugBreakHook )
|
|
{
|
|
_stepHandler = null;
|
|
_serverSideStepperIsSet = false;
|
|
_interrupt = false;
|
|
}
|
|
|
|
// Keep going if the interrupt no longer needed.
|
|
if( hook instanceof CtrlInterruptHook &&
|
|
null == _stepHandler && ! _interrupt && ! _serverSideStepperIsSet )
|
|
{
|
|
_semaphore.release();
|
|
return;
|
|
}
|
|
|
|
// save arguments
|
|
|
|
_threadState = debug;
|
|
_pc = pc;
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "6) about to call _pc.getSourceLocation()");
|
|
_sourceLocation = (JSSourceLocation) _pc.getSourceLocation();
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "7) _pc.getSourceLocation() returned");
|
|
|
|
if(false)
|
|
{
|
|
String leadin = "interrupted at:";
|
|
String url = _sourceLocation.getURL()+"#"+(_sourceLocation.getLine());
|
|
String fun = (null != _pc.getScript().getFunction()) ? (_pc.getScript().getFunction()+"()") : "toplevel";
|
|
String script = "["+(_pc.getScript().getBaseLineNumber())+","+
|
|
(_pc.getScript().getBaseLineNumber()+_pc.getScript().getLineExtent()-1)+"]";
|
|
String where = "pc: " + _pc.getPC();
|
|
|
|
if(AS.DEBUG){System.out.println(leadin+" "+url+" "+fun+" "+script+" "+where);}
|
|
// if(AS.DEBUG)Thread.dumpStack();
|
|
}
|
|
|
|
// Hitting any other type of hook clears the interrupt stepper
|
|
if( ! (hook instanceof CtrlInterruptHook) )
|
|
{
|
|
_stepHandler = null;
|
|
_serverSideStepperIsSet = false;
|
|
}
|
|
|
|
// If there is a step handler then let it process the hook
|
|
if( null != _stepHandler )
|
|
{
|
|
switch( _stepHandler.step(_threadState,_pc,_sourceLocation,hook) )
|
|
{
|
|
case StepHandler.STOP:
|
|
_stepHandler = null;
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "8) StepHandler.STOP");
|
|
break;
|
|
case StepHandler.CONTINUE_SEND_INTERRUPT:
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "9) StepHandler.CONTINUE_SEND_INTERRUPT about to sendInterrupt");
|
|
_dc.sendInterrupt();
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "10) sendInterrupt returned");
|
|
_semaphore.release();
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "10.5) _semaphore.release() returned");
|
|
return;
|
|
case StepHandler.CONTINUE_DONE:
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "11) StepHandler.CONTINUE_DONE");
|
|
_stepHandler = null;
|
|
_semaphore.release();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if(AS.DEBUG){System.out.println( "about to send HIT_EXEC_HOOK" );}
|
|
// if(AS.DEBUG){System.out.println( "this = " + this );}
|
|
// if(AS.DEBUG){System.out.println( "HIT_EXEC_HOOK = " + HIT_EXEC_HOOK );}
|
|
// if(AS.DEBUG){System.out.println( "hook = " + hook );}
|
|
// if(AS.DEBUG){System.out.println( "Thread = " + Thread.currentThread() );}
|
|
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "12) about to call _threadState.leaveSuspended()");
|
|
_threadState.leaveSuspended();
|
|
if(AS.DEBUG)Log.trace("ControlTyrant.aboutToExecute", "13) _threadState.leaveSuspended() returned");
|
|
|
|
// post message to UI thread
|
|
_app.performCommandLater( this, HIT_EXEC_HOOK, hook );
|
|
}
|
|
|
|
// This where we receive commands from the CommandPoster
|
|
// implement target interface
|
|
public synchronized void performCommand(String cmd, Object data)
|
|
{
|
|
if( ! _enabled )
|
|
{
|
|
_state = STOPPED;
|
|
_continueAndNotify(false);
|
|
return;
|
|
}
|
|
if( cmd.equals(HIT_EXEC_HOOK) )
|
|
{
|
|
SourceTyrant st = _emperor.getSourceTyrant();
|
|
if( null == st ||
|
|
null == _sourceLocation ||
|
|
null == _sourceLocation.getURL() ||
|
|
null == st.findSourceItem(_sourceLocation.getURL()) )
|
|
{
|
|
if(AS.DEBUG){System.out.println( "continuing: source text unavailable");}
|
|
_state = STOPPED;
|
|
_continueAndNotify(false);
|
|
return;
|
|
}
|
|
|
|
_emperor.setWaitCursor(true);
|
|
_interrupt = false;
|
|
|
|
// if(AS.DEBUG){System.out.println( "got stop command, transitioning state" );}
|
|
|
|
_emperor.bringAppToFront();
|
|
|
|
// transition to STOPPED state
|
|
_state = STOPPED;
|
|
_notifyOfStateChange();
|
|
_emperor.setWaitCursor(false);
|
|
}
|
|
else if( cmd.equals(HIT_ERROR_REPORTER) )
|
|
{
|
|
_emperor.bringAppToFront();
|
|
Sound.soundNamed("droplet.au").play();
|
|
// AWTCompatibility.awtToolkit().beep();
|
|
|
|
ErrorReporterDialog erd =
|
|
new ErrorReporterDialog(_emperor, (ErrorReport) data);
|
|
|
|
_emperor.enableAppClose(false);
|
|
erd.showModally();
|
|
_emperor.enableAppClose(true);
|
|
|
|
_debugBreakResponse = erd.getAnswer();
|
|
notify();
|
|
}
|
|
else
|
|
{
|
|
if(AS.S)ER.T(false,"unhandled command received in perform command: " + cmd,this);
|
|
}
|
|
|
|
}
|
|
|
|
// implement observer interface
|
|
public void update(Observable o, Object arg)
|
|
{
|
|
|
|
}
|
|
|
|
/*
|
|
* NOTE: this ErrorReporter may be called on a thread other than
|
|
* the IFC UI thread
|
|
*/
|
|
public synchronized JSErrorReporter setErrorReporter(JSErrorReporter er)
|
|
{
|
|
JSErrorReporter old = _errorReporter;
|
|
_errorReporter = er;
|
|
return old;
|
|
|
|
}
|
|
public JSErrorReporter getErrorReporter()
|
|
{
|
|
return null != _errorReporter ? _errorReporter : this ;
|
|
}
|
|
|
|
// implement JSErrorReporter interface
|
|
public int reportError( String msg,
|
|
String filename,
|
|
int lineno,
|
|
String linebuf,
|
|
int tokenOffset )
|
|
{
|
|
if( ! _enabled )
|
|
return JSErrorReporter.PASS_ALONG;
|
|
|
|
if( null != _errorReporter && this != _errorReporter )
|
|
return _errorReporter.reportError(msg,filename,lineno,linebuf,tokenOffset);
|
|
|
|
synchronized(this)
|
|
{
|
|
ErrorReport er = new ErrorReport( msg, filename, lineno,
|
|
linebuf, tokenOffset );
|
|
|
|
_debugBreakResponse = -1;
|
|
_app.performCommandLater( this, HIT_ERROR_REPORTER, er );
|
|
|
|
while( -1 == _debugBreakResponse && _enabled )
|
|
{
|
|
try
|
|
{
|
|
wait();
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
if(AS.DEBUG){System.out.println("threw during wait for command response");}
|
|
if(AS.DEBUG){System.out.println(e);}
|
|
}
|
|
}
|
|
if( -1 == _debugBreakResponse )
|
|
_debugBreakResponse = JSErrorReporter.PASS_ALONG;
|
|
|
|
return _debugBreakResponse;
|
|
}
|
|
}
|
|
|
|
private void _notifyOfStateChange()
|
|
{
|
|
if(AS.S)ER.T(Thread.currentThread()==_uiThreadForAssertCheck,"_notifyObservers called on thread other than UI thread",this);
|
|
setChanged();
|
|
notifyObservers( new ControlTyrantUpdate( ControlTyrantUpdate.STATE_CHANGED,_state) );
|
|
}
|
|
|
|
private void _notifyOfDebuggerDisabled()
|
|
{
|
|
if(AS.S)ER.T(Thread.currentThread()==_uiThreadForAssertCheck,"_notifyObservers called on thread other than UI thread",this);
|
|
setChanged();
|
|
notifyObservers( new ControlTyrantUpdate( ControlTyrantUpdate.DEBUGGER_DISABLED,_state) );
|
|
}
|
|
|
|
|
|
// data...
|
|
private Application _app;
|
|
private int _state;
|
|
private boolean _interrupt;
|
|
private Emperor _emperor;
|
|
private DebugController _dc;
|
|
private CtrlSemaphore _semaphore;
|
|
private JSThreadState _threadState;
|
|
private JSPC _pc;
|
|
private JSSourceLocation _sourceLocation;
|
|
|
|
private StepHandler _stepHandler;
|
|
|
|
private CtrlInterruptHook _interruptHook;
|
|
private CtrlDebugBreakHook _debugBreakHook;
|
|
private int _debugBreakResponse;
|
|
|
|
private boolean _enabled = true;
|
|
private Thread _uiThreadForAssertCheck = null;
|
|
private JSErrorReporter _errorReporter;
|
|
|
|
private boolean _useServerSideStepper = false;
|
|
private boolean _serverSideStepperIsSet = false;
|
|
private int _serverSideStepperType;
|
|
|
|
private int _evaluatingBreakpoint = 0;
|
|
|
|
private final String HIT_EXEC_HOOK = "HIT_EXEC_HOOK";
|
|
private final String HIT_ERROR_REPORTER = "HIT_ERROR_REPORTER";
|
|
|
|
}
|
|
|
|
/***************************************************************************/
|
|
// used internally only...
|
|
class CtrlSemaphore
|
|
{
|
|
public boolean available() {return _available;}
|
|
public synchronized void release() {_available = true;}
|
|
public synchronized boolean grab()
|
|
{
|
|
if(! _available)
|
|
return false;
|
|
_available = false;
|
|
return true;
|
|
}
|
|
|
|
private boolean _available = true;
|
|
}
|
|
|
|
// used internally only...
|
|
class CtrlInterruptHook
|
|
extends InterruptHook
|
|
implements ChainableHook
|
|
{
|
|
CtrlInterruptHook( ControlTyrant ctrlTyrant )
|
|
{
|
|
setTyrant( ctrlTyrant );
|
|
}
|
|
|
|
public void aboutToExecute(ThreadStateBase debug, PC pc)
|
|
{
|
|
// System.out.println( "called the right hook..." );
|
|
|
|
// for safety we make sure not to throw anything at native caller.
|
|
try {
|
|
if( null != _ctrlTyrant )
|
|
{
|
|
if(AS.S)ER.T(debug instanceof JSThreadState,"wrong kind of threadstate",this);
|
|
if(AS.S)ER.T(pc instanceof JSPC,"wrong kind of pc",this);
|
|
_ctrlTyrant.aboutToExecute( (JSThreadState)debug, (JSPC) pc, this );
|
|
}
|
|
if( null != _nextHook )
|
|
_nextHook.aboutToExecute(debug, pc);
|
|
} catch(Throwable t){t.printStackTrace();} // eat it.
|
|
}
|
|
|
|
// implement ChainableHook
|
|
public void setTyrant(Object tyrant) {_ctrlTyrant = (ControlTyrant) tyrant;}
|
|
public void setNextHook(Hook nextHook) {_nextHook = (InterruptHook) nextHook;}
|
|
|
|
private ControlTyrant _ctrlTyrant;
|
|
private InterruptHook _nextHook;
|
|
|
|
}
|
|
|
|
// used internally only...
|
|
class CtrlDebugBreakHook
|
|
extends DebugBreakHook
|
|
implements ChainableHook
|
|
{
|
|
CtrlDebugBreakHook( ControlTyrant ctrlTyrant )
|
|
{
|
|
setTyrant( ctrlTyrant );
|
|
}
|
|
|
|
public void aboutToExecute(ThreadStateBase debug, PC pc)
|
|
{
|
|
// for safety we make sure not to throw anything at native caller.
|
|
try {
|
|
if( null != _ctrlTyrant )
|
|
{
|
|
if(AS.S)ER.T(debug instanceof JSThreadState,"wrong kind of threadstate",this);
|
|
if(AS.S)ER.T(pc instanceof JSPC,"wrong kind of pc",this);
|
|
_ctrlTyrant.aboutToExecute( (JSThreadState)debug, (JSPC) pc, this );
|
|
}
|
|
if( null != _nextHook )
|
|
_nextHook.aboutToExecute(debug, pc);
|
|
} catch(Throwable t){} // eat it.
|
|
}
|
|
|
|
// implement ChainableHook
|
|
public void setTyrant(Object tyrant) {_ctrlTyrant = (ControlTyrant) tyrant;}
|
|
public void setNextHook(Hook nextHook) {_nextHook = (DebugBreakHook) nextHook;}
|
|
|
|
private ControlTyrant _ctrlTyrant;
|
|
private DebugBreakHook _nextHook;
|
|
}
|
|
|