/************************************************************************** * * @@@BUILDINFO@@@ 72debugger-2.jsx 2.0.1.70 05-June-2007 * Copyright 2006-2007 Adobe Systems Incorporated * All Rights Reserved. * * NOTICE: All information contained herein is, and remains the property of * Adobe Systems Incorporated and its suppliers, if any. The intellectual * and technical concepts contained herein are proprietary to Adobe Systems * Incorporated and its suppliers and may be covered by U.S. and Foreign * Patents,patents in process,and are protected by trade secret or copyright * law. Dissemination of this information or reproduction of this material * is strictly forbidden unless prior written permission is obtained from * Adobe Systems Incorporated. **************************************************************************/ // The current debugger contains the debugger that has either been selected // by the user, or by a request connect command from a target application. var currentDebugger = null; // for internal use var internalBT = { count:0 }; /////////////////////////////////////////////////////////////////////////////// // // Debugger broadcaster // Debugger.broadcaster = new Broadcaster; Debugger.registerClient = function( clientObj ) { return Debugger.broadcaster.registerClient( clientObj ); } Debugger.unregisterClient = function( clientObj ) { Debugger.broadcaster.unregisterClient( clientObj ); } Debugger.notifyClients = function( reason, param01, param02 ) { Debugger.broadcaster.notifyClients( reason, param01, param02 ); } // register for Debugger broadcasts Debugger.registerClient( menus.debug ); //----------------------------------------------------------------------------- // // setupDebugger(...) // // Purpose: Initialize the debugger subsystem. Return false on errors. // //----------------------------------------------------------------------------- function setupDebugger( remote ) { Debugger.connect ("estoolkit-2.0", null, 0); return true; } /////////////////////////////////////////////////////////////////////////////// // // Debugger class: // // The Debugger object is the core object that talks to the debugging // backend. There is one Debugger object per active connection. // // Debugger status values Debugger.RUNNING = localize ("$$$/ESToolkit/Toolbar/Engine/State/Running=running"); Debugger.STOPPED = localize ("$$$/ESToolkit/Toolbar/Engine/State/Stopped=stopped"); Debugger.WAITING = localize ("$$$/ESToolkit/Toolbar/Engine/State/Waiting=waiting"); Debugger.INACTIVE = ""; // The list of all debuggers. Debugger.all = []; // The list of all connected applications. This is an object whose // properties are the BridgeTalk names of all connected apps with // the value set to true. Debugger.connected = {}; //----------------------------------------------------------------------------- // // Debugger(...) // // Purpose: ctor // //----------------------------------------------------------------------------- function Debugger (target, engine) { this.parent = currentDebugger; // in case the engine is bad this.target = target; // the target name this.engine = engine; // the engine name this.dynamic = false; // true if the engine is created on the fly this.waiting = false; // true if the engine state is "waiting" this.error = null; // on runtime errors, the error message this.document = null; // the doc being debugged this.frame = -1; // the stack frame we are looking at during debugging this.stack = ""; // the complete stack trace; force a redisplay of variables if changed this.line = -1; // the current line this.documents = {}; // active documents collection; properties are script IDs this.profileLevel = 0; // the profiling level this.oldStack = false; // for targets with versions <= 3.6.36 this.resetLevel = false; // If set to true, send breakpoints and continue this.setState( Debugger.INACTIVE ); // for now: first the static, then the dynamic // this.dictionary = app.loadDictionary (target); // if (!this.dictionary) // this.dictionary = new TargetDictionary (target); Debugger.all.push (this); } // The documents object holds information about the documents currently being debugged. // Each property (which is the document's scriptID) contains an object with the following // properties: // document: the Document object // profData: the Document profiler data (array of objects, objects containing the props line, hit, time) //----------------------------------------------------------------------------- // // function(...) // // Purpose: [static] Connect to a target app. This call attempts to launch the target, // identifies the app as the debugger, collects the list of engines, // creates the necessary debugger instances, and activates the debugger // on the first engine returned. The supplied name is the BridgeTalk name. // If doc and dbgLevel are supplied, the connect request was due to an // autoconnect from within Debugger.start(). We need to fire a Start commend // with these two arguments if present. // //----------------------------------------------------------------------------- Debugger.connect = function( target, doc, dbgLevel ) { if( !currentDebugger || ( currentDebugger && ( currentDebugger.target != target || !targetMgr.getConnected( target ) ) ) ) { var busyID = false; if( doc ) busyID = doc.busyID; globalBroadcaster.notifyClients( 'startConnect', target ); if( !Debugger.doConnect( target, doc, dbgLevel, busyID, Debugger ) ) { app.stopBusyFor( busyID ); globalBroadcaster.notifyClients( 'endConnect', target ); } } else if( currentDebugger ) targetMgr.setActive( currentDebugger.target, currentDebugger.engine, doc ); } Debugger.doConnect = function( target, doc, dbgLevel, busyID, clientObj ) { var ret = false; if( busyID ) { this.busyID = busyID; app.startBusyFor( busyID ); } if( !BridgeTalk.findInstance( target, 'Connect' ) ) { targetMgr.checkTarget( target ); var bt = BridgeTalk.create( target, "Connect", true ); bt.doc = doc; bt.dbgLevel = dbgLevel; bt.dbg = this; bt.busyID = this.busyID; bt.client = clientObj; bt.onOwnError = function (bt) { if( this.busyID ) { app.stopBusyFor( this.busyID ); delete this.busyID; } // strip the debugger suffix if present var target = this.target.replace( "#estk", "" ); if( this.client && this.client.onConnected ) this.client.onConnected( false, target, this.doc ); globalBroadcaster.notifyClients( 'endConnect', target, this.dbg ); } bt.onOwnResult = function (bt) { if( this.busyID ) { app.stopBusyFor( this.busyID ); delete this.busyID; } var engines = Debugger.setupConnection( bt, false, this.client ); // strip the debugger suffix if present var target = this.target.replace( "#estk", "" ); // tell the TargetManager targetMgr.addEngines( target ); if( this.client && this.client.onConnected ) this.client.onConnected( true, target, engines, this.dbgLevel, this.doc ); // // remove dummy Debugger instances without engine // for( var i=Debugger.all.length-1; i>=0; i-- ) { if( Debugger.all[i].target == target && !Debugger.all[i].engine ) Debugger.all.splice(i,1); } globalBroadcaster.notifyClients( 'endConnect', target, this.dbg ); } ret = bt.safeSend(); } return ret; } Debugger.onConnected = function( connected, target, engines, dbgLevel, doc ) { if( connected ) { if( target != 'estoolkit-2.0' ) { app.toFront(); } /* if( currentDebugger.target == target && doc ) { if( engines == 1 ) currentDebugger.start( doc, dbgLevel ); else { var displayName = targetMgr.getTargetDisplayName( target ); messageBox ("$$$/ESToolkit/Alerts/PleaseSwitch=Please select target %1!", displayName); // TODO // window.engines.active = true; } } */ } } Debugger.connectSynchronous = function( target, doc, dbgLevel ) { function SyncHandler( targetName ) { this.targetName = targetName; this.finished = true; } SyncHandler.prototype.onNotify = function( reason ) { if( reason == 'startConnect' && arguments[1] == this.targetName ) this.finished = false; if( reason == 'endConnect' && arguments[1] == this.targetName ) this.finished = true; } var syncObj = new SyncHandler( target ); function checkFinished() { return !syncObj.finished; } globalBroadcaster.registerClient( syncObj ); Debugger.connect( target, doc, dbgLevel ); wait( checkFinished ); globalBroadcaster.unregisterClient( syncObj ); } //----------------------------------------------------------------------------- // // remoteConnect(...) // // Purpose: [static] Execute a remote connection request. // //----------------------------------------------------------------------------- Debugger.remoteConnect = function(bt) { // // setup the connection // Debugger.setupConnection( bt, true/*, Debugger*/ ); // don't pass a callback obj so that onSetupConnection isn't called! // // probably we had no chance to collect all targets yet, // so add it to the TargetManager // var target = bt.sender.replace( "#estk", "" ); var engine = bt.headers.Engine; var displayName = BridgeTalk.getDisplayName( target ); targetMgr.addTarget( target, displayName, true ); targetMgr.addEngines( target ); OMVData.load (target); } //----------------------------------------------------------------------------- // // disconnect(...) // // Purpose: [static] Disconnect all active debuggers from a target; called when // the target goes down. // //----------------------------------------------------------------------------- Debugger.disconnect = function (target, quiet) { if( targetMgr.getConnected( target ) ) { targetMgr.setConnected( target, false ); var lastDebugger = currentDebugger; for (var i = 0; i < Debugger.all.length; i++) { var dbg = Debugger.all [i]; if (dbg.target == target) { if( dbg.state != Debugger.INACTIVE ) { // for the first active debugger on a target, display the error message if (!quiet) { errorBox ("$$$/ESToolkit/Alerts/TargetDead=%1 does not respond anymore!^nThe debugging session will be aborted.", targetMgr.getTargetDisplayName (target)); quiet = true; } dbg.stop(true); } Debugger.all.splice(i,1); i--; } } targetMgr.targetDied( target ); } } //----------------------------------------------------------------------------- // // getAll(...) // // Purpose: [static] Get all debuggers for a given target. // //----------------------------------------------------------------------------- Debugger.getAll = function (target) { var debuggers = []; for (var i = 0; i < Debugger.all.length; i++) { var dbg = Debugger.all [i]; if (dbg.target == target) debuggers.push (dbg); } return debuggers; } Debugger.setCurrent = function( dbg ) { currentDebugger = dbg; } //----------------------------------------------------------------------------- // // setupConnection(...) // // Purpose: [static] Setup a debugger connection by reading the body of the given // BT message. This message is either a received ConnectRequest // message, or the reply of a Connect message. This will also // activate this debugger. Returns the number of engines discovered. // //----------------------------------------------------------------------------- Debugger.setupConnection = function( bt, remote, clientObj ) { var reply = bt.splitBody(); var target = bt.sender.replace ("#estk", ""); var selEngine = bt.headers.Engine; var selDebugger = null; var engines = 0; targetMgr.setConnected( target, true ); for( var i=0; i 1 ) { features = features[1].split( ',' ); for( var i=0; i 0 ) engine = engines[0]; } } if( !engine || engine.length == 0 ) engine = this.engine; } if( !target ) { // // no target given: use this debugger // target = this.target; if (!engine) engine = this.engine; } // // access the internal engine for testing purpose // with target directive estoolkit#dbg // if( target && target == 'estoolkit#dbg' ) { print( eval( doc.text ) ); return; /* var bt = new BridgeTalk; bt.target = target; bt.body = doc.text; bt.index = internalBT.count; internalBT[internalBT.count++] = bt; bt.onResult = bt.onError = bt.onTimeout = function(bt) { print( bt.body ); internalBT[this.index] = null; } bt.send(); return; */ } if( target ) targetDebugger = Debugger.find (target, engine); else targetDebugger = null; if (!targetDebugger) { if( targetMgr.getConnected( target ) ) { targetDebugger = new Debugger( target, engine ); /* // The target does not support the given target/engine var displayName = targetMgr.getTargetDisplayName (target); if (!displayName) displayName = target; if (engine.length) displayName += " (" + engine + ")"; errorBox ("$$$/ESToolkit/Alerts/CannotConnect=Cannot connect to target %1!", displayName); */ } else { // no idea if the target contains the engine. // Connect and have start() called again. Debugger.connectSynchronous( target, doc, dbgLevel ); var delayedSrc = 'if(targetMgr.getConnected("' + target + '"))currentDebugger.start( Document.find("'+doc.scriptID+'"),'+dbgLevel+','+saveFiles+');'; app.scheduleTask( delayedSrc, 1, false ); return; } } else { if( !targetMgr.getConnected( targetDebugger.target ) ) { // // oops...no connection... // setup connection synchron and continue if successfull // Debugger.connectSynchronous( targetDebugger.target, doc, dbgLevel ); var delayedSrc = 'if(targetMgr.getConnected("' + targetDebugger.target + '"))currentDebugger.start( Document.find("'+doc.scriptID+'"),'+dbgLevel+','+saveFiles+');'; app.scheduleTask( delayedSrc, 1, false ); return; } } if( !this.isActive() ) { // OK, we came here, so we have a target debugger. // This may not be the same as "this", because the doc may have had // #target / #targetengine defined... targetDebugger.startSession (doc, dbgLevel); } } } //----------------------------------------------------------------------------- // // startSession(...) // // Purpose: Start a debug session without checking any #target / #targetengine // in the document. // //----------------------------------------------------------------------------- Debugger.prototype.startSession = function (doc, dbgLevel) { if( dbgLevel == undefined ) dbgLevel = 0; // // If the debug level is 1 (call debugger on break) then set // debug level to 2 (call debugger immediately) to set breakpoints // again (for targets that create engines on the fly) // if( dbgLevel == 1 ) { dbgLevel = 2; this.resetLevel = true; } // // if this isn't the current debugger then switch to this // (switch back if execution failed) // var oldCurrent = null; var oldTarget = null; var oldEngine = null; if( this != currentDebugger ) { oldCurrent = currentDebugger; Debugger.setCurrent( this ); oldTarget = targetMgr.getActiveTarget(); oldEngine = ( oldTarget ? oldTarget.getActive() : null ); targetMgr.setActiveEngine( this.target, this.engine, true ); } this.profileLevel = PrefUtils.getValue( 'prefs.profiling.profileLevel', 'Number' ); if( this.profileLevel ) if( doc ) doc.clearProfileData(); else this.profileLevel = 0; this.setState( Debugger.RUNNING ); this.error = null; this.line = -1; this.clearDocuments(); this.setDocument( doc ); Document.setStatusLine( '', doc ); var bt = BridgeTalk.create (this.target, "Eval"); bt.headers.Engine = this.engine; bt.headers.DebugLevel = dbgLevel; bt.headers.ScriptID = doc.scriptID; bt.headers.Profiling = this.profileLevel; bt.headers.DebugFlags = PrefUtils.getValue( 'prefs.debug.dontBreakOnErrors', 'Boolean' ) ? 1024 : 0; // add the debugger for onResult bt.dbg = this; bt.onOwnResult = function (bt) { this.dbg.stop (true); // the reply is datatype,result var reply = bt.splitBody(); if( reply && reply.length && reply[0].length ) print (reply [0][1]); } bt.onOwnError = function (bt) { this.dbg.stop (true); // display the error message // if there is no line number and script name info, use an alert // but not if the error is "Execution Halted" var errCode = parseInt (bt.headers ["Error-Code"]); if (!bt.headers.ScriptID && errCode != -34) { // if the error code is 32 (kErrBadAction), assume that there // was no engine to execute in. var msg = bt.body; if (errCode == 32) { msg = localize ("$$$/ESToolkit/Error/MissingEngine=Cannot execute script in target engine '%1'!", this.headers.Engine); // reset the current debugger to the parent of this one if (this.dbg.parent) Debugger.setCurrent (this.dbg.parent); } else if (msg == "ENGINE BUSY") msg = localize ("$$$/ESToolkit/Error/EngineBusy=Target engine '%1' is busy!", this.headers.Engine); errorBox (msg); } } // send the text if the doc is modified or out of sync with the disk image if( doc.isModified() ) bt.body = doc.text; this.currentBusyID = doc.busyID; app.startBusyFor( this.currentBusyID, false ); if( bt.safeSend() ) { // show that this debugger is active this.activate(); } else { // // switch back to previous current debugger // if( oldCurrent ) Debugger.setCurrent( oldCurrent ); if( oldTarget ) targetMgr.setActiveEngine( oldTarget.targetName, oldEngine, true ); // // stop debugger // bt.onOwnError(bt); } /* // // eval command via XML // if( dbgLevel == undefined ) dbgLevel = 0; // // if this isn't the current debugger then switch to this // (switch back if execution failed) // var oldCurrent = null; var oldTarget = null; var oldEngine = null; if( this != currentDebugger ) { oldCurrent = currentDebugger; Debugger.setCurrent( this ); oldTarget = targetMgr.getActiveTarget(); oldEngine = ( oldTarget ? oldTarget.getActive() : null ); targetMgr.setActiveEngine( this.target, this.engine, true ); } this.profileLevel = PrefUtils.getValue( 'prefs.profiling.profileLevel', 'Number' ); if( this.profileLevel ) { if( doc ) doc.clearProfileData(); } else this.profileLevel = 0; this.setState( Debugger.RUNNING ); this.error = null; this.line = -1; this.clearDocuments(); this.setDocument( doc ); Document.setStatusLine( '', doc ); var cmd = new XML( '' ); var targetObj = targetMgr.findTarget( this.target ); if( targetObj ) { var bps = targetObj.getBreakpointsXML( this.engine, ( PrefUtils.getValue( 'prefs.debug.dontBreakOnErrors', 'Boolean' ) ? 1024 : 0 ) ); if( bps ) cmd.appendChild( bps ); } // send the text if the doc is modified or out of sync with the disk image if( doc.isModified() ) { var src = new XML( '' + doc.text + '' ); cmd.appendChild( src ); } var bt = BridgeTalk.create( this.target ); bt.body = cmd.toXMLString(); // add the debugger for onResult bt.dbg = this; bt.onOwnResult = function (bt) { // // TODO: format of result (XML?) // this.dbg.stop (true); // the reply is datatype,result var reply = bt.splitBody(); if( reply && reply.length && reply[0].length ) print (reply [0][1]); } bt.onOwnError = function (bt) { // // TODO: format of result (XML?) // this.dbg.stop (true); // display the error message // if there is no line number and script name info, use an alert // but not if the error is "Execution Halted" var errCode = parseInt (bt.headers ["Error-Code"]); if (!bt.headers.ScriptID && errCode != -34) { // if the error code is 32 (kErrBadAction), assume that there // was no engine to execute in. var msg = bt.body; if (errCode == 32) { msg = localize ("$$$/ESToolkit/Error/MissingEngine=Cannot execute script in target engine '%1'!", this.headers.Engine); // reset the current debugger to the parent of this one if (this.dbg.parent) Debugger.setCurrent (this.dbg.parent); } else if (msg == "ENGINE BUSY") msg = localize ("$$$/ESToolkit/Error/EngineBusy=Target engine '%1' is busy!", this.headers.Engine); errorBox (msg); } } this.currentBusyID = doc.busyID; app.startBusyFor( this.currentBusyID, false ); if( bt.safeSend() ) { // show that this debugger is active this.activate(); } else { // // switch back to previous current debugger // if( oldCurrent ) Debugger.setCurrent( oldCurrent ); if( oldTarget ) targetMgr.setActiveEngine( oldTarget.targetName, oldEngine, true ); // // stop debugger // bt.onOwnError(bt); } */ } //----------------------------------------------------------------------------- // // stop(...) // // Purpose: Stop debugging. Get the final profiling data, the final variables, // and clear the stack trace display. // //----------------------------------------------------------------------------- Debugger.prototype.stop = function (dontHalt) { app.stopBusyFor( this.currentBusyID ); // halt the engine if (this.isActive()) { if (!dontHalt) this.execute ("Halt"); } if (this.profileLevel) this.getProfData (true); this.setStoppedState(); // no stack display callstack.erase(); this.getVariables (true); app.toFront(); } //----------------------------------------------------------------------------- // // setState(...) // // Purpose: Set the state of the debugger with reflection into the UI. // //----------------------------------------------------------------------------- Debugger.prototype.setState = function (state) { // // cannot set a state of Inactive if the engine is in Waiting state // if( this.waiting && state == Debugger.INACTIVE ) state = Debugger.WAITING; var oldstate = this.state; this.state = state; if( this == currentDebugger && oldstate != this.state ) Debugger.notifyClients( 'state', this, oldstate, this.state ); } //----------------------------------------------------------------------------- // // setStoppedState(...) // // Purpose: Put this debugger into a stopped state without sending any messages. // //----------------------------------------------------------------------------- Debugger.prototype.setStoppedState = function() { this.setState (Debugger.INACTIVE); this.error = null; this.frame = -1; this.stack = ""; this.line = -1; if (this.document) this.document.setCurrentLine (this.line, 0xFFFFFF); this.clearDocuments(); } //----------------------------------------------------------------------------- // // eval(...) // // Purpose: Do a eval of the supplied text without any breakpoints etc. // //----------------------------------------------------------------------------- Debugger.prototype.eval = function (text, noReply) { // this BridgeTalk instance does not carry any script ID var bt = BridgeTalk.create (this.target, "Eval"); bt.headers.Profiling = this.profileLevel; bt.headers.Engine = this.engine; bt.body = text; bt.dbg = this; bt.noReply = noReply; bt.onResult = function (bt) { // the reply is datatype,result if (!this.noReply) { var reply = bt.splitBody(); var result = ''; if( reply && reply[0] ) result = reply[0][1]; print (localize ("$$$/ESToolkit/Panes/Console/Result=Result:"), ' ', result ); } this.dbg.getVariables(); this.destroy(); } bt.onOwnError = function (bt) { // the reply is the message print (localize ("$$$/ESToolkit/Panes/Console/Error=Error:"), ' ', bt.body); app.beep(); } bt.onOwnTimeout = function(bt) { // ignore timeout } bt.safeSend(); } //----------------------------------------------------------------------------- // // getVariables(...) // // Purpose: Get the variables of the current scope. // //----------------------------------------------------------------------------- Debugger.prototype.getVariables = function (all) { // get only for engines that are either not dynamic, or that are active if (!this.dynamic || this.state != Debugger.INACTIVE) databrowser.getVariables (this.target, this.engine, (this.state != Debugger.STOPPED), all); else databrowser.erase(); } //----------------------------------------------------------------------------- // // getProfData(...) // // Purpose: Get the profiling data of this execution. // //----------------------------------------------------------------------------- Debugger.prototype.getProfData = function (erase) { if (!this.profileLevel) return; var bt = BridgeTalk.create (this.target, "ProfilerData"); bt.headers.Engine = this.engine; if (erase) bt.headers.Clear = "1"; bt.dbg = this; bt.onOwnResult = function (bt) { if (this.dbg.profileLevel) { var docObj = null; var prevScriptID = null; var reply = bt.splitBody(); // fill this object with existing data for easier access var profDataObjs = []; if (document.profdata) { for (var i = 0; i < document.profData.length; i++) { var profObj = document;profData[i]; profDataObjs [profObj.line] = profObj; } } for (var i = 0; i < reply.length; i++) { // line, time, hits, function, module var line = +reply [i][0]; var time = +reply [i][1]; var hits = +reply [i][2]; // var func = reply [i][3]; var scriptID = reply [i][4]; // this is only transmitted on change if (scriptID) { // get that doc if (!docObj || prevScriptID != scriptID) { docObj = this.dbg.documents [scriptID]; if (!docObj) { doc = Document.find (scriptID); if (!doc) continue; // doc seems to be closed already docObj = this.dbg.documents [scriptID] = { document:doc, line:-1 }; } // set the profiler data for the previous doc if (prevScriptID) this.dbg.restoreProfData (prevScriptID); prevScriptID = scriptID; } } if (docObj) { if( !docObj.profData ) docObj.profData = new Array; var entry = profDataObjs [line]; if( entry ) { entry.hit = hits; entry.time = time; } else { var profObj = { line : line, hit : hits, time : time }; docObj.profData.push( profObj ); profDataObjs [line] = profObj; } } } // show for the last doc if (prevScriptID) this.dbg.restoreProfData (prevScriptID); } } bt.safeSend(); } //----------------------------------------------------------------------------- // // clearProfData(...) // // Purpose: Clear the profiling data for this debugger during deactivation. // If the script ID is given, just restore the data for the given document. // //----------------------------------------------------------------------------- Debugger.prototype.clearProfData = function (scriptID) { if (this.profileLevel) { if (scriptID) { // for a specific document var docObj = this.documents [scriptID]; if (docObj) docObj.document.clearProfileData(); } else { for (var scriptID in this.documents) this.clearProfData (scriptID); } } } //----------------------------------------------------------------------------- // // restoreProfData(...) // // Purpose: Restore the code profiler data for this debugger during activation. // If the script ID is given, just restore data for the given document. // //----------------------------------------------------------------------------- Debugger.prototype.restoreProfData = function (scriptID) { if (this.profileLevel) { if (scriptID) { // for a specific document var docObj = this.documents [scriptID]; if( docObj && docObj.profData ) { docObj.document.profileData = docObj.profData; docObj.document.updateProfData(); } } else { for (var scriptID in this.documents) this.restoreProfData (scriptID); } } } //----------------------------------------------------------------------------- // // getHelpTip(...) // // Purpose: Get the help tip text for the given text. // //----------------------------------------------------------------------------- Debugger.prototype.getHelpTip = function (doc, line, text) { // Can only do help tips if not running if the engine is not dynamic if (this.dynamic && !this.isActive()) { doc.helpTip = ""; return; } if (this.error && line == this.line) // display the error doc.helpTip = this.error; else if (text.length && !app.checkSyntax (text).error) { // we create a simple Eval command and ignore any errors var bt = BridgeTalk.create (this.target, "Eval"); bt.headers.Engine = this.engine; // we want the target to walk the stack until we have a result bt.headers.WalkStack = 1; // no script ID - we supply the script on the fly bt.doc = doc; bt.text = text; bt.dbg = this; // the result handler creates something like 'type name = value' // and sets the help tip bt.onOwnResult = function (bt) { // the doc attached to the BridgeTalk instance var doc = this.doc; if (doc) { // the reply of eval is datatype,result var reply = bt.splitBody(); var dataType = ''; var result = ''; if( reply && reply.length && reply[0].length ) { dataType = reply [0][0]; result = reply [0][1]; } if (dataType == "Function") text = dataType + ' ' + this.text; else if (dataType != "undefined") { if (dataType == "string") { result = '"' + app.escape (result) + '"'; // Truncate a string after 20 characters if (result.length > 20) result = result.substr (0, 20) + '"...'; } text = dataType + ' ' + this.text + ' = ' + result; } else if (this.text == "undefined") text = ""; doc.helpTip = text; } } // the error handler displays the error bt.onOwnError = function (bt) { // the doc attached to the BridgeTalk instance if (this.doc) this.doc.helpTip = bt.body; } bt.body = text; bt.safeSend(); } else doc.helpTip = ""; } //----------------------------------------------------------------------------- // // doContinue(...) // // Purpose: Continue the execution after a breakpoint. // If the debugger is inactive, start a session. // //----------------------------------------------------------------------------- Debugger.prototype.doContinue = function (doc, dbgLevel) { if (dbgLevel == undefined) dbgLevel = 1; Document.setStatusLine( '', doc ); if (this.state == Debugger.RUNNING) return; if (this.state == Debugger.STOPPED) { // clear the current line if (this.document) this.document.setCurrentLine (this.line, 0xFFFFFF); this.execute ("Continue"); } else { // execute the given document // first, check the syntax var result = doc.compile( doc.includePath ); // a compiled script starts with '@' if (result[0] == '@') // if OK, send over to the target this.start (doc, dbgLevel); else { // otherwise, display the error and beep Document.setStatusLine( result, doc ); app.beep(); } } } //----------------------------------------------------------------------------- // // execute(...) // // Purpose: Start/continue debugging by issuing the given command if the // debugger is active. // //----------------------------------------------------------------------------- Debugger.prototype.execute = function (cmd) { if (cmd == "Continue" && this.state != Debugger.STOPPED) return; var bt = BridgeTalk.create (this.target, cmd); bt.headers.Engine = this.engine; bt.headers.Profiling = this.profileLevel; bt.headers.IgnoreErrors = 0; bt.headers.DebugFlags = PrefUtils.getValue( 'prefs.debug.dontBreakOnErrors', 'Boolean' ) ? 1024 : 0; bt.dbg = this; if (this.document) // the doc may not be there if there was a load error bt.headers.ScriptID = this.document.scriptID; // ask whether to clear any runtime error first if (cmd != "Halt") { if (this.error && queryBox ("$$$/ESToolkit/Alerts/ClearErrors=Clear runtime error?")) { bt.headers.IgnoreErrors = 1; } if (this.document) // re-attach existing breakpoints targetMgr.sendBreakpoints( this.target, this.engine ); } this.setState (Debugger.RUNNING); this.error = null; bt.safeSend(); // Indicate the execution by switching the document to front if (this.document) this.document.activate(); } //----------------------------------------------------------------------------- // // loadScript(...) // // Purpose: Load a script from the target. // //----------------------------------------------------------------------------- Debugger.prototype.loadScript = function (scriptID) { scripts.loadScript (this.target, this.engine, ScriptID, targetMgr.getTargetDisplayName (this.target), this); } //----------------------------------------------------------------------------- // // showNextStatement(...) // // Purpose: Scroll the current document to the current line // //----------------------------------------------------------------------------- Debugger.prototype.showNextStatement = function() { if (this.state == Debugger.STOPPED) callstack.switchToBottom(); } //----------------------------------------------------------------------------- // // switchFrame(...) // // Purpose: In halt mode, switch to the given stack frame // //----------------------------------------------------------------------------- Debugger.prototype.switchFrame = function (frame) { if (this.frame != frame) { var bt = BridgeTalk.create (this.target, "SwitchFrame"); bt.headers.Engine = this.engine; if (this.oldStack) frame = callstack.getFrames() - frame; bt.body = frame; bt.safeSend(); } } //----------------------------------------------------------------------------- // // processMsg(...) // // Purpose: Process a received message // //----------------------------------------------------------------------------- Debugger.prototype.processMsg = function (bt) { switch (bt.headers.Command) { case "Print": // print msg to console console.print (bt.body); break; case "Breakpoints": // list of moved or removed breakpoints // shouldn't happen any more var doc = Document.find (bt.headers.ScriptID); if (doc) doc.updateBreakpoints (bt); break; case "Error": // runtime error this.error = bt.headers.ErrorMessage.replace (/\\n-/g, " "); if( this.error == "ENGINE BUSY" ) { errorBox( "$$$/ESToolkit/Messages/EngineBusy=The target engine is currently busy." ); break; } else { // if the debugger is active, this is a halt at a throw if (this.state == Debugger.RUNNING && bt.headers.ErrorCode == 54) this.error = localize ("$$$/ESToolkit/Messages/ExceptionThrown=JavaScript exception thrown"); Document.setStatusLine(this.error); app.beep(); } // fall thru case "Break": // breakpoint hit case "Frame": // stack frame changed { // If in a modal state, do not stop if (app.modalState) { this.state = Debugger.STOPPED; this.execute ("Continue"); break; } // Create an object containing all important information. var breakData = {}; breakData.line = parseInt (bt.headers.CurrentLine); breakData.frame = parseInt (bt.headers.Frame); breakData.frame = ((isNaN( breakData.frame ) || breakData.frame < 0) ? 0 : breakData.frame); breakData.source = null; breakData.allVars = false; breakData.stack = null; if( bt.headers.Command != "Frame" ) { // // the body contains the stack trace, a newline, and the source // var srcStart = bt.body.indexOf ("\n\n"); breakData.stack = bt.body; if( srcStart >= 0 ) { breakData.stack = bt.body.substr( 0, srcStart + 1 ); breakData.source = bt.body.substr( srcStart + 2 ); } // // need to redisplay all vars if the stack changed // breakData.allVars = (breakData.stack != this.stack); } // // set up the document // breakData.doc = this.document; if( !breakData.doc || ( bt.headers.ScriptID.length > 0 && breakData.doc.scriptID != bt.headers.ScriptID ) ) breakData.doc = Document.find( bt.headers.ScriptID ); var newDocCreated = false; if (!breakData.doc) { // // no doc yet - it came from the target // if( File( bt.headers.ScriptID ).exists ) { var docWin = scripts.loadFile( bt.headers.ScriptID ); if( docWin ) breakData.doc = docWin.document; } else if( breakData.source ) // supplied by break { var docWin = Document.create( this.title, breakData.source, bt.headers.ScriptID ); if( docWin ) breakData.doc = docWin.document; } else { // no source supplied (pre-x41): set off an async get script request scripts.loadScript( bt.headers.ScriptID, this.title, this.target, this.engine ); // may be there already... breakData.doc = Document.find (bt.headers.ScriptID); } newDocCreated = ( breakData.doc != this.document ); if( newDocCreated ) breakData.doc.window.visible = false; } // OK, the breakData has all relevant data: // line - the line number // frame - the stack frame index // doc - the document (may be generated on the fly) // allVars - true to reload all variables into the Data Browser if( breakData.doc ) { breakData.doc.activate(); var targetObj = targetMgr.findTarget( this.target ); if( targetObj ) { targetObj.setActive( this.engine ); breakData.doc.selectActiveTarget( targetObj ) } } else break; if( this.resetLevel ) { // // If the flag resetLevel is set, then the script was started // to run. We got the break here to set breakpoints again and // continue (for targets that create engines on the fly) // this.resetLevel = false; targetMgr.sendBreakpoints( this.target, this.engine ); // Only continue if this is not a real breakpoint if (!breakData.doc.getBreakpoint( breakData.line )) { if( newDocCreated ) breakData.doc.close(); this.state = Debugger.STOPPED; this.execute ("Continue"); break; // done with processing } } // If we came here, the debugger needs to be activated, and // the breakpoint needs to be displayed if( breakData.doc ) breakData.doc.window.visible = true; this.activate(); this.setState (Debugger.STOPPED); this.line = breakData.line; this.frame = breakData.frame; if (breakData.stack) { this.stack = breakData.stack; callstack.update( breakData.stack ); } if( this.currentBusyID && breakData.doc != this.document ) { if( app.isBusy( this.currentBusyID ) ) app.stopBusyFor( this.currentBusyID ); this.currentBusyID = null; } this.setDocument(breakData.doc); if( !this.currentBusyID ) { this.currentBusyID = breakData.doc.busyID; if( !app.isBusy( this.currentBusyID ) ) app.startBusyFor( this.currentBusyID ); } // Get local variables, get all variables if the stack trace changed this.getVariables (breakData.allVars); // and get the profiler data if (this.profileLevel) this.getProfData (false); app.toFront(); } break; case "Exit": this.stop (true); break; } } //----------------------------------------------------------------------------- // // setDocument(...) // // Purpose: Set the current document and highlighting. // //----------------------------------------------------------------------------- Debugger.prototype.setDocument = function (doc, noErrors) { var color = colors.LightGreen; var errorMsg = ""; if (this.frame == 0 && !noErrors) { color = colors.Yellow; if (this.error) { color = colors.Coral; errorMsg = this.error; } } Document.setStatusLine( errorMsg, doc ); if (this.line >= 0) { doc.setCurrentLine (this.line, color); // scroll to that line doc.setSelection (this.line, 0, true); } // Add the doc to my documents list if not present var docObj = this.documents [doc.scriptID]; if(!docObj) this.documents [doc.scriptID] = docObj = { document:doc, profData:null }; // bring the document to front if (this.document != doc) { if( this.document ) this.document.setCurrentLine( this.line, 0xFFFFFF ); this.document = doc; } if (doc) doc.activate(); } //----------------------------------------------------------------------------- // // closeDocument(...) // // Purpose: Remove a document from the debugger because it is about to being closed. // //----------------------------------------------------------------------------- Debugger.prototype.closeDocument = function (doc) { delete this.documents [doc.scriptID]; if (this.document == doc) this.document = null; } Debugger.prototype.getDocumentsLength = function() { var num = 0; for( i in this.documents ) num++; return num; } //----------------------------------------------------------------------------- // // isDocumentActive(...) // // Purpose: Check if the given document is actively being debugged. // //----------------------------------------------------------------------------- Debugger.prototype.isDocumentActive = function (doc) { return (this.isActive() && this.documents [doc.scriptID] && (this.documents [doc.scriptID].document == doc)); } //----------------------------------------------------------------------------- // // clearDocuments(...) // // Purpose: Erase all documents. // //----------------------------------------------------------------------------- Debugger.prototype.clearDocuments = function() { this.document = this.lines = null; this.documents = {}; } //----------------------------------------------------------------------------- // // toFront(...) // // Purpose: Request a bring-to-front. // //----------------------------------------------------------------------------- Debugger.prototype.toFront = function() { // up to now every client supports that (pwollek:01/24/2007) if (_win) { var bt = BridgeTalk.create (this.target, "ToFront"); bt.headers.ID = app.id; bt.send(); return true; } else // Non-Windows: signal app to use BridgeTalk return false; } //----------------------------------------------------------------------------- // // getCodeHints (text) // // Purpose: Return an array of code hints. The argument is a text string, which // can either be a class name, an element name, or a class name, a dot and an // element name. If an OMV for the target app is present, an array of text strings // is returned that match possible elements. Use this API mainly to find elements // unless you are sure about the class. // //----------------------------------------------------------------------------- Debugger.prototype.getCodeHints = function (text) { return OMVData.getCodeHints( this.target, this.engine, text ); } //----------------------------------------------------------------------------- // // displaySelectedCodeHint (text) // // Purpose: Call this API with the original text selected by the user to cause // the OMV UI to update its display to the user selection. // //----------------------------------------------------------------------------- Debugger.prototype.displaySelectedCodeHint = function (text) { return OMVData.displaySelectedCodeHint( this.target, this.engine, text ); }