blob: 588a97760a3bfaa81f1166f42dceee8a63e00a8c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package flex.tools.debugger.cli;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PrintStream;
import java.io.StringReader;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
import flash.localization.LocalizationManager;
import flash.tools.debugger.Bootstrap;
import flash.tools.debugger.DebuggerLocalizer;
import flash.tools.debugger.Frame;
import flash.tools.debugger.InProgressException;
import flash.tools.debugger.Isolate;
import flash.tools.debugger.IsolateSession;
import flash.tools.debugger.Location;
import flash.tools.debugger.NoResponseException;
import flash.tools.debugger.NotConnectedException;
import flash.tools.debugger.NotSupportedException;
import flash.tools.debugger.NotSuspendedException;
import flash.tools.debugger.PlayerDebugException;
import flash.tools.debugger.Session;
import flash.tools.debugger.SessionManager;
import flash.tools.debugger.SourceFile;
import flash.tools.debugger.SourceLocator;
import flash.tools.debugger.SuspendReason;
import flash.tools.debugger.SuspendedException;
import flash.tools.debugger.SwfInfo;
import flash.tools.debugger.Value;
import flash.tools.debugger.Variable;
import flash.tools.debugger.VariableAttribute;
import flash.tools.debugger.VariableType;
import flash.tools.debugger.VersionException;
import flash.tools.debugger.Watch;
import flash.tools.debugger.WatchKind;
import flash.tools.debugger.concrete.DProtocol;
import flash.tools.debugger.events.BreakEvent;
import flash.tools.debugger.events.ConsoleErrorFault;
import flash.tools.debugger.events.DebugEvent;
import flash.tools.debugger.events.ExceptionFault;
import flash.tools.debugger.events.FaultEvent;
import flash.tools.debugger.events.FileListModifiedEvent;
import flash.tools.debugger.events.FunctionMetaDataAvailableEvent;
import flash.tools.debugger.events.IsolateCreateEvent;
import flash.tools.debugger.events.IsolateExitEvent;
import flash.tools.debugger.events.SwfLoadedEvent;
import flash.tools.debugger.events.SwfUnloadedEvent;
import flash.tools.debugger.events.TraceEvent;
import flash.tools.debugger.expression.ECMA;
import flash.tools.debugger.expression.NoSuchVariableException;
import flash.tools.debugger.expression.PlayerFaultException;
import flash.tools.debugger.expression.ValueExp;
import flash.util.FieldFormat;
import flash.util.Trace;
import flex.tools.debugger.cli.ExpressionCache.EvaluationResult;
import flex.tools.debugger.cli.FaultActions.FaultActionsBuilder;
/**
* This is a front end command line interface to the Flash Debugger
* Player.
*<p>
* This tool utilizes the Debugger Java API (DJAPI) for Flash
* Player that exists in flash.tools.debuggger.
*<p>
* This tool is not completely compliant with the API, since
* some commands expose implementation specific information for
* debugging purposes. Instances where this occurs are kept to a
* minimum and are isolated in a special class called Extensions.
* If you wish to build a version that is completely API
* compatible. Replace Extensions with ExtensionsDisabled in
* the static method calls at the end of this file.
*/
public class DebugCLI implements Runnable, SourceLocator
{
public static final String VERSION = "82"; //$NON-NLS-1$
public static final int CMD_UNKNOWN = 0;
public static final int CMD_QUIT = 1;
public static final int CMD_CONTINUE = 2;
public static final int CMD_STEP = 3;
public static final int CMD_NEXT = 4;
public static final int CMD_FINISH = 5;
public static final int CMD_BREAK = 6;
public static final int CMD_SET = 7;
public static final int CMD_LIST = 8;
public static final int CMD_PRINT = 9;
public static final int CMD_TUTORIAL = 10;
public static final int CMD_INFO = 11;
public static final int CMD_HOME = 12;
public static final int CMD_RUN = 13;
public static final int CMD_FILE = 14;
public static final int CMD_DELETE = 15;
public static final int CMD_SOURCE = 16;
public static final int CMD_COMMENT = 17;
public static final int CMD_CLEAR = 18;
public static final int CMD_HELP = 19;
public static final int CMD_SHOW = 20;
public static final int CMD_KILL = 21;
public static final int CMD_HANDLE = 22;
public static final int CMD_ENABLE = 23;
public static final int CMD_DISABLE = 24;
public static final int CMD_DISPLAY = 25;
public static final int CMD_UNDISPLAY = 26;
public static final int CMD_COMMANDS = 27;
public static final int CMD_PWD = 28;
public static final int CMD_CF = 29;
public static final int CMD_CONDITION = 30;
public static final int CMD_AWATCH = 31;
public static final int CMD_WATCH = 32;
public static final int CMD_RWATCH = 33;
public static final int CMD_WHAT = 34;
public static final int CMD_DISASSEMBLE = 35;
public static final int CMD_HALT = 36;
public static final int CMD_MCTREE = 37;
public static final int CMD_VIEW_SWF = 38;
public static final int CMD_DOWN = 39;
public static final int CMD_UP = 40;
public static final int CMD_FRAME = 41;
public static final int CMD_DIRECTORY = 42;
public static final int CMD_CATCH = 43;
public static final int CMD_CONNECT = 44;
public static final int CMD_WORKER = 45;
/* info sub commands */
public static final int INFO_UNKNOWN_CMD = 100;
public static final int INFO_ARGS_CMD = 101;
public static final int INFO_BREAK_CMD = 102;
public static final int INFO_FILES_CMD = 103;
public static final int INFO_HANDLE_CMD = 104;
public static final int INFO_FUNCTIONS_CMD = 105;
public static final int INFO_LOCALS_CMD = 106;
public static final int INFO_SCOPECHAIN_CMD = 107;
public static final int INFO_SOURCES_CMD = 108;
public static final int INFO_STACK_CMD = 109;
public static final int INFO_VARIABLES_CMD = 110;
public static final int INFO_DISPLAY_CMD = 111;
public static final int INFO_TARGETS_CMD = 112;
public static final int INFO_SWFS_CMD = 113;
public static final int INFO_WORKERS_CMD = 114;
/* show subcommands */
public static final int SHOW_UNKNOWN_CMD = 200;
public static final int SHOW_NET_CMD = 201;
public static final int SHOW_FUNC_CMD = 202;
public static final int SHOW_URI_CMD = 203;
public static final int SHOW_PROPERTIES_CMD = 204;
public static final int SHOW_FILES_CMD = 205;
public static final int SHOW_BREAK_CMD = 206;
public static final int SHOW_VAR_CMD = 207;
public static final int SHOW_MEM_CMD = 208;
public static final int SHOW_LOC_CMD = 209;
public static final int SHOW_DIRS_CMD = 210;
/* misc subcommands */
public static final int ENABLE_ONCE_CMD = 301;
// default metadata retry count 8 attempts per waitForMetadata() call * 5 calls
public static final int METADATA_RETRIES = 8*5;
/* Enum for the state of the initial prompt shown when a swf is loaded */
public static enum InitialPromptState { NEVER_SHOWN, SHOWN_ONCE, DONE }
Stack<LineNumberReader> m_readerStack = new Stack<LineNumberReader>();
public PrintStream m_err;
public PrintStream m_out;
Session m_session;
String m_launchURI;
boolean m_fullnameOption; // emacs mode
String m_cdPath;
String m_mruURI;
String m_connectPort;
public final static String m_newline = System.getProperty("line.separator"); //$NON-NLS-1$
private final static LocalizationManager m_localizationManager = new LocalizationManager();
private final static FaultActionsBuilder faultActionsBuilder = new FaultActionsBuilder(m_localizationManager);
List<String> m_sourceDirectories; // List of String
int m_sourceDirectoriesChangeCount;
private File m_flexHomeDirectory; // <application.home>/frameworks/projects/*/src always goes in m_sourceDirectories
private boolean m_initializedFlexHomeDirectory;
// context information for our current session
FileInfoCache m_fileInfo;
FaultActions m_faultTable;
Vector<Integer> m_breakIsolates;
ExpressionCache m_exprCache;
Vector<BreakAction> m_breakpoints;
Vector<WatchAction> m_watchpoints;
Vector<CatchAction> m_catchpoints;
ArrayList<DisplayAction> m_displays;
// boolean m_requestResume;
// boolean m_requestHalt;
// boolean m_stepResume;
int m_activeIsolate;
DebugCLIIsolateState m_mainState;
/* This indicates the isolate for which we have been showing prompts for setting
* breakpoints( so that we don't switch worker while the user is setting breakpoints) */
int m_lastPromptIsolate;
private HashMap<Integer, DebugCLIIsolateState> m_isolateState;
class DebugCLIIsolateState
{
// public FileInfoCache m_fileInfo;
// public ExpressionCache m_exprCache;
public boolean m_requestResume;
public boolean m_requestHalt;
public boolean m_stepResume;
/* Indicates whether the prompt for setting initial breakpoints has been displayed for this isolate */
public InitialPromptState m_promptState;
// public Vector<BreakAction> m_breakpoints;
// public Vector<WatchAction> m_watchpoints;
// public Vector<CatchAction> m_catchpoints;
// public ArrayList<DisplayAction> m_displays;
public DebugCLIIsolateState(DebugCLI debugcli) {
// m_exprCache = new ExpressionCache(debugcli);
m_faultTable = faultActionsBuilder.build();//new FaultActions();
// m_breakpoints = new Vector<BreakAction>();
// m_watchpoints = new Vector<WatchAction>();
// m_catchpoints = new Vector<CatchAction>();
// m_displays = new ArrayList<DisplayAction>();
}
}
private DebugCLIIsolateState getIsolateState(int isolateId) {
if (isolateId == Isolate.DEFAULT_ID)
return m_mainState;
DebugCLIIsolateState isolateState = null;
if (!m_isolateState.containsKey(isolateId)) {
isolateState = new DebugCLIIsolateState(this);
m_isolateState.put(isolateId, isolateState);
}
else
isolateState = m_isolateState.get(isolateId);
return isolateState;
}
public int getActiveIsolateId() {
return m_activeIsolate;
}
private boolean getRequestResume(int isolateId) {
return getIsolateState(isolateId).m_requestResume;
}
private void setRequestResume(boolean value, int isolateId) {
getIsolateState(isolateId).m_requestResume = value;
}
private boolean getStepResume(int isolateId) {
return getIsolateState(isolateId).m_stepResume;
}
private void setStepResume(boolean value, int isolateId) {
getIsolateState(isolateId).m_stepResume = value;
}
private boolean getRequestHalt(int isolateId) {
return getIsolateState(isolateId).m_requestHalt;
}
private void setRequestHalt(boolean value, int isolateId) {
getIsolateState(isolateId).m_requestHalt = value;
}
private InitialPromptState getPromptState(int isolateId) {
return getIsolateState(isolateId).m_promptState;
}
private void setPromptState(InitialPromptState value, int isolateId) {
getIsolateState(isolateId).m_promptState = value;
}
/* our current input processing context */
LineNumberReader m_in;
public LineNumberReader m_keyboardStream;
Vector<String> m_keyboardInput;
boolean m_keyboardReadRequest;
StringTokenizer m_currentTokenizer;
String m_currentToken;
String m_currentLine;
public String m_repeatLine;
/**
* The module that the next "list" command should display if no
* module is explicitly specified.
*/
public static final String LIST_MODULE = "$listmodule"; //$NON-NLS-1$
/**
* The line number at which the next "list" command should begin if no
* line number is explicitly specified.
*/
public static final String LIST_LINE = "$listline"; //$NON-NLS-1$
public static final String LIST_WORKER = "$listworker"; //$NON-NLS-1$
/**
* The number of lines displayed by the "list" command.
*/
private static final String LIST_SIZE = "$listsize"; //$NON-NLS-1$
private static final String COLUMN_WIDTH = "$columnwidth"; //$NON-NLS-1$
private static final String UPDATE_DELAY = "$updatedelay"; //$NON-NLS-1$
private static final String HALT_TIMEOUT = "$halttimeout"; //$NON-NLS-1$
/**
* Current breakpoint number.
*/
private static final String BPNUM = "$bpnum"; //$NON-NLS-1$
/**
* Used to determine how much context information should be displayed.
*/
private static final String LAST_FRAME_DEPTH = "$lastframedepth"; //$NON-NLS-1$
/**
* Used to determine how much context information should be displayed.
*/
private static final String CURRENT_FRAME_DEPTH = "$currentframedepth"; //$NON-NLS-1$
/**
* The current frame we are viewing -- controlled by the "up", "down", and "frame" commands.
*/
public static final String DISPLAY_FRAME_NUMBER = "$displayframenumber"; //$NON-NLS-1$
private static final String FILE_LIST_WRAP = "$filelistwrap"; //$NON-NLS-1$
private static final String NO_WAITING = "$nowaiting"; //$NON-NLS-1$
/**
* Show this pointer for info stack.
*/
private static final String INFO_STACK_SHOW_THIS = "$infostackshowthis"; //$NON-NLS-1$
/**
* Number of milliseconds to wait for metadata.
*/
private static final String METADATA_ATTEMPTS_PERIOD = "$metadataattemptsperiod"; //$NON-NLS-1$
private static final String METADATA_NOT_AVAILABLE = "$metadatanotavailable"; //$NON-NLS-1$
/**
* How many times we should try to get metadata.
*/
private static final String METADATA_ATTEMPTS = "$metadataattempts"; //$NON-NLS-1$
private static final String PLAYER_FULL_SUPPORT = "$playerfullsupport"; //$NON-NLS-1$
/**
* Whether the "print" command will display attributes of members.
*/
public static final String DISPLAY_ATTRIBUTES = "$displayattributes"; //$NON-NLS-1$
/* class's static init */
static
{
// set up for localizing messages
m_localizationManager.addLocalizer( new DebuggerLocalizer("flex.tools.debugger.cli.fdb.") ); //$NON-NLS-1$
}
public static void main(String[] args)
{
DebugCLI cli = new DebugCLI();
/* attach our 'main' input method and out/err*/
cli.m_err = System.err;
cli.m_out = System.out;
// get the default <application.home>/projects/frameworks/*/src entries into the source path
cli.initSourceDirectoriesList();
// a big of wrangling for our keyboard input stream since its special
cli.m_keyboardStream = new LineNumberReader(new InputStreamReader(System.in));
cli.pushStream(cli.m_keyboardStream);
/* iterate through the args list */
cli.processArgs(args);
/* figure out $HOME and the current directory */
String userHome = System.getProperty("user.home"); //$NON-NLS-1$
String userDir = System.getProperty("user.dir"); //$NON-NLS-1$
/*
* If the current directory is not $HOME, and a .fdbinit file exists in the current directory,
* then push it onto the stack of files to read.
*
* Note, we want ./.fdbinit to be read AFTER $HOME/.fdbinit, but we push them in reverse
* order, because they're going onto a stack. If we push them in reverse order, then they
* will be read in the correct order (last one pushed is the first one read).
*/
if (userDir != null && !userDir.equals(userHome))
{
try
{
FileReader sr = new FileReader(new File(userDir, ".fdbinit")); //$NON-NLS-1$
cli.pushStream( new LineNumberReader(sr) );
}
catch(FileNotFoundException fnf) {}
}
/*
* If a .fdbinit file exists in the $HOME directory, then push it onto the stack of files
* to read.
*
* Note, we want ./.fdbinit to be read AFTER $HOME/.fdbinit, but we push them in reverse
* order, because they're going onto a stack. If we push them in reverse order, then they
* will be read in the correct order (last one pushed is the first one read).
*/
if (userHome != null)
{
try
{
FileReader sr = new FileReader(new File(userHome, ".fdbinit")); //$NON-NLS-1$
cli.pushStream( new LineNumberReader(sr) );
}
catch(FileNotFoundException fnf) {}
}
cli.execute();
}
public DebugCLI()
{
m_fullnameOption = false;
m_faultTable = faultActionsBuilder.build();
m_exprCache = new ExpressionCache(this);
m_breakpoints = new Vector<BreakAction>();
m_watchpoints = new Vector<WatchAction>();
m_catchpoints = new Vector<CatchAction>();
m_displays = new ArrayList<DisplayAction>();
m_keyboardInput = new Vector<String>();
m_mruURI = null;
m_sourceDirectories = new LinkedList<String>();
initProperties();
m_mainState = new DebugCLIIsolateState(this);
m_lastPromptIsolate = -1;
initIsolateState();
}
public static LocalizationManager getLocalizationManager() { return m_localizationManager; }
public Session getSession() { return m_session; }
public FileInfoCache getFileCache() {
return m_fileInfo;
}
/**
* Convert a module to class name. This is used
* by the ExpressionCache to find variables
* that live at royale package scope. That
* is variables such as mx.core.Component.
*/
public String module2ClassName(int moduleId)
{
String pkg = null;
try
{
SourceFile file = m_fileInfo.getFile(moduleId);
pkg = file.getPackageName();
}
catch(Exception npe)
{
// didn't work ignore it.
}
return pkg;
}
LineNumberReader popStream() { return m_readerStack.pop(); }
public void pushStream(LineNumberReader r) { m_readerStack.push(r); }
boolean haveStreams() { return !m_readerStack.empty(); }
public void processArgs(String[] args)
{
for(int i=0; i<args.length; i++)
{
String arg = args[i];
// System.out.println("arg["+i+"]= '"+arg+"'");
if (arg.charAt(0) == '-')
{
// its an option
if (arg.equals("-unit")) // unit-testing mode //$NON-NLS-1$
{
System.setProperty("fdbunit", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
else if (arg.equals("-fullname") || arg.equals("-f")) //$NON-NLS-1$ //$NON-NLS-2$
{
m_fullnameOption = true; // emacs mode
}
else if (arg.equals("-cd")) //$NON-NLS-1$
{
// consume the path
if (i+1 < args.length)
m_cdPath = args[i++];
}
else if (arg.equals("-p")) //$NON-NLS-1$
{
// consume the port
if (i+1 < args.length)
m_connectPort = args[++i];
}
else
{
err("Unknown command-line argument: " + arg); //$NON-NLS-1$
}
}
else
{
// its a URI to run
StringReader sr = new StringReader("run "+arg+m_newline); //$NON-NLS-1$
pushStream( new LineNumberReader(sr) );
}
}
}
/**
* Dispose of the current line and read the next from the current stream, if its an empty
* line and we are console then repeat last line.
*/
protected String readLine() throws IOException
{
String line = null;
if (haveStreams())
line = m_in.readLine();
else
line = keyboardReadLine();
setCurrentLine(line);
return line;
}
/**
* The reader portion of our keyboard input routine
* Block until input arrives.
*/
synchronized String keyboardReadLine()
{
// enable a request then block on the queue
m_keyboardReadRequest = true;
try { wait(); } catch(InterruptedException ie) {}
// pull from the front of the queue
return m_keyboardInput.remove(0);
}
/**
* A seperate thread collects our input so that we can
* block in the doContinue on the main thread and then
* allow the user to interrupt us via keyboard input
* on this thread.
*
* We built the stupid thing in this manner, since readLine()
* will block no matter what and if we 'quit' we can't
* seem to kill this thread. .close() doesn't work
* and Thread.stop(), etc. all fail to do the job.
*
* Thus we needed to take a request response approach
* so that we only block when requested to do so.
*/
public void run()
{
// while we have this stream
while(m_keyboardStream != null)
{
try
{
// only if someone is requesting us to read do we do so...
if (m_keyboardReadRequest)
{
// block on keyboard input and put it onto the end of the queue
String s = m_keyboardStream.readLine();
m_keyboardInput.add(s);
// fullfilled request, now notify blocking thread.
m_keyboardReadRequest = false;
synchronized(this) { notifyAll(); }
}
else
try { Thread.sleep(50); } catch(InterruptedException ie) {}
}
catch(IOException io)
{
// io.printStackTrace();
}
}
}
public void setCurrentLine(String s)
{
m_currentLine = s;
if (m_currentLine == null)
m_currentTokenizer = null; /* eof */
else
{
m_currentLine = m_currentLine.trim();
/* if nothing provided on this command then pull our 'repeat' command */
if (m_repeatLine != null && !haveStreams() && m_currentLine.length() == 0)
m_currentLine = m_repeatLine;
m_currentTokenizer = new StringTokenizer(m_currentLine, " \n\r\t"); //$NON-NLS-1$
}
}
/* Helpers for extracting tokens from the current line */
public boolean hasMoreTokens() { return m_currentTokenizer.hasMoreTokens(); }
public String nextToken() { m_currentToken = m_currentTokenizer.nextToken(); return m_currentToken; }
public int nextIntToken() throws NumberFormatException { nextToken(); return Integer.parseInt(m_currentToken); }
public long nextLongToken() throws NumberFormatException { nextToken(); return Long.parseLong(m_currentToken); }
public String restOfLine() { return m_currentTokenizer.nextToken("").trim(); } //$NON-NLS-1$
public void execute()
{
/* dump console message */
displayStartMessage();
/* now fire our keyboard input thread */
Thread t = new Thread(this, "Keyboard input"); //$NON-NLS-1$
t.start();
/* keep processing streams until we have no more to do */
while(haveStreams())
{
try
{
m_in = popStream();
process();
}
catch(EOFException eof)
{
; /* quite allright */
}
catch(IOException io)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("exceptionMessage", io); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("errorWhileProcessingFile", args)); //$NON-NLS-1$
}
}
/* we done kill everything */
exitSession();
// clear this thing, which also halts our other thread.
m_keyboardStream = null;
}
public PrintStream getOut() { return m_out; }
private void displayStartMessage()
{
String build = getLocalizationManager().getLocalizedTextString("defaultBuildName"); //$NON-NLS-1$
try
{
Properties p = new Properties();
p.load(this.getClass().getResourceAsStream("version.properties")); //$NON-NLS-1$
String buildString = p.getProperty("build"); //$NON-NLS-1$
if ((buildString != null) && (! buildString.equals(""))) //$NON-NLS-1$
{
build = buildString;
}
}
catch (Throwable t)
{
// ignore
}
Map<String, Object> aboutMap = new HashMap<String, Object>(); aboutMap.put("build", build); //$NON-NLS-1$
out(getLocalizationManager().getLocalizedTextString("about", aboutMap)); //$NON-NLS-1$
out(getLocalizationManager().getLocalizedTextString("copyright")); //$NON-NLS-1$
}
void displayPrompt()
{
m_out.print("(fdb) "); //$NON-NLS-1$
}
void displayCommandPrompt()
{
m_out.print(">"); //$NON-NLS-1$
}
// add the given character n times to sb
void repeat(StringBuilder sb, char c, int n)
{
while(n-- > 0)
sb.append(c);
}
// Prompt the user to respond to a yes or no type question
boolean yesNoQuery(String prompt) throws IOException
{
boolean result = false;
m_out.print(prompt);
m_out.print(getLocalizationManager().getLocalizedTextString("yesOrNoAppendedToAllQuestions")); //$NON-NLS-1$
String in = readLine();
if (in != null && in.equals(getLocalizationManager().getLocalizedTextString("singleCharacterUserTypesForYes"))) //$NON-NLS-1$
result = true;
else if (in != null && in.equals("escape")) //$NON-NLS-1$
throw new IllegalArgumentException("escape"); //$NON-NLS-1$
else
out(getLocalizationManager().getLocalizedTextString("yesNoQueryNotConfirmed")); //$NON-NLS-1$
return result;
}
public void err(String s)
{
// Doesn't make sense to send messages to stderr, because this is
// an interactive application; and besides that, sending a combination
// of interwoven but related messages to both stdout and stderr causes
// the output to be in the wrong order sometimes.
out(s);
}
public void out(String s)
{
if (s.length() > 0 && (s.charAt(s.length()-1) == '\n') )
m_out.print(s);
else
m_out.println(s);
}
static String uft()
{
Runtime rt = Runtime.getRuntime();
long free = rt.freeMemory(), total = rt.totalMemory(), used = total - free;
// long max = rt.maxMemory();
java.text.NumberFormat nf = java.text.NumberFormat.getInstance() ;
// System.out.println("used: "+nf.format(used)+" free: "+nf.format(free)+" total: "+nf.format(total)+" max: "+nf.format(max));
return "Used "+nf.format(used)+" - free "+nf.format(free)+" - total "+nf.format(total); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Add all properties that we know about
*/
void initProperties()
{
propertyPut(LIST_SIZE, 10);
propertyPut(LIST_LINE, 1);
propertyPut(LIST_MODULE, 1); // default to module #1
propertyPut(LIST_WORKER, Isolate.DEFAULT_ID);
propertyPut(COLUMN_WIDTH, 70);
propertyPut(UPDATE_DELAY, 25);
propertyPut(HALT_TIMEOUT, 7000);
propertyPut(BPNUM, 0); // set current breakpoint number as something bad
propertyPut(LAST_FRAME_DEPTH, 0); // used to determine how much context information should be displayed
propertyPut(CURRENT_FRAME_DEPTH, 0); // used to determine how much context information should be displayed
propertyPut(DISPLAY_FRAME_NUMBER, 0); // houses the current frame we are viewing
propertyPut(FILE_LIST_WRAP, 999999); // default 1 file name per line
propertyPut(NO_WAITING, 0);
propertyPut(INFO_STACK_SHOW_THIS, 1); // show this pointer for info stack
}
// getter/setter for properties; in the expression cache, so that they can be used in expressions!
public void propertyPut(String k, int v) { m_exprCache.put(k,v); }
public int propertyGet(String k) { return ((Integer)m_exprCache.get(k)).intValue(); }
public Set<String> propertyKeys() { return m_exprCache.keySet(); }
/**
* Process this reader until its done
*/
void process() throws IOException
{
boolean done = false;
while(!done)
{
try
{
/**
* Now if we are in a session and that session is suspended then we go
* into a state where we wait for some user interaction to get us out
*/
runningLoop();
/* if we are in the stdin then put out a prompt */
if (!haveStreams())
displayPrompt();
/* now read in the next line */
readLine();
if (m_currentLine == null)
break;
done = processLine();
}
catch(NoResponseException nre)
{
err(getLocalizationManager().getLocalizedTextString("noResponseException")); //$NON-NLS-1$
}
catch(NotSuspendedException nse)
{
err(getLocalizationManager().getLocalizedTextString("notSuspendedException")); //$NON-NLS-1$
}
catch(AmbiguousException ae)
{
// we already put up a warning for the user
}
catch(IllegalStateException ise)
{
err(getLocalizationManager().getLocalizedTextString("illegalStateException")); //$NON-NLS-1$
}
catch(IllegalMonitorStateException ime)
{
err(getLocalizationManager().getLocalizedTextString("illegalMonitorStateException")); //$NON-NLS-1$
}
catch(NoSuchElementException nse)
{
err(getLocalizationManager().getLocalizedTextString("noSuchElementException")); //$NON-NLS-1$
}
catch(NumberFormatException nfe)
{
err(getLocalizationManager().getLocalizedTextString("numberFormatException")); //$NON-NLS-1$
}
catch(SocketException se)
{
Map<String, Object> socketArgs = new HashMap<String, Object>();
socketArgs.put("message", se.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("socketException", socketArgs)); //$NON-NLS-1$
}
catch(VersionException ve)
{
err(getLocalizationManager().getLocalizedTextString("versionException")); //$NON-NLS-1$
}
catch(NotConnectedException nce)
{
// handled by isConnectionLost()
}
catch(Exception e)
{
err(getLocalizationManager().getLocalizedTextString("unexpectedError")); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("stackTraceFollows")); //$NON-NLS-1$
e.printStackTrace();
}
// check for a lost connection and if it is clean-up!
if (isConnectionLost())
{
try
{
dumpHaltState(false);
}
catch(PlayerDebugException pde)
{
err(getLocalizationManager().getLocalizedTextString("sessionEndedAbruptly")); //$NON-NLS-1$
}
}
}
}
// check if we have lost the connect without our help...
boolean isConnectionLost()
{
boolean lost = false;
if (m_session != null && !m_session.isConnected())
lost = true;
return lost;
}
boolean haveConnection()
{
boolean have = false;
if (m_session != null && m_session.isConnected())
have = true;
return have;
}
void doShow() throws AmbiguousException, PlayerDebugException
{
/* show without any args brings up help */
if (!hasMoreTokens())
out( getHelpTopic("show") ); //$NON-NLS-1$
else
{
/* otherwise we have a boatload of options */
String subCmdString = nextToken();
int subCmd = showCommandFor(subCmdString);
switch(subCmd)
{
case SHOW_NET_CMD:
doShowStats();
break;
case SHOW_FUNC_CMD:
doShowFuncs();
break;
case SHOW_URI_CMD:
doShowUri();
break;
case SHOW_PROPERTIES_CMD:
doShowProperties();
break;
case SHOW_FILES_CMD:
doShowFiles();
break;
case SHOW_BREAK_CMD:
doShowBreak();
break;
case SHOW_VAR_CMD:
doShowVariable();
break;
case SHOW_MEM_CMD:
doShowMemory();
break;
case SHOW_LOC_CMD:
doShowLocations();
break;
case SHOW_DIRS_CMD:
doShowDirectories();
break;
default:
doUnknown("show", subCmdString); //$NON-NLS-1$
break;
}
}
}
void doShowUri()
{
// dump the URI that the player has sent us
try
{
StringBuilder sb = new StringBuilder();
sb.append("URI = "); //$NON-NLS-1$
sb.append( m_session.getURI() );
out( sb.toString() );
}
catch(Exception e)
{
err(getLocalizationManager().getLocalizedTextString("noUriReceived")); //$NON-NLS-1$
}
}
/**
* Dump the content of files in a raw format
*/
void doShowFiles()
{
try
{
StringBuilder sb = new StringBuilder();
for (Isolate isolate : m_session.getWorkers()) {
Iterator itr = m_fileInfo.getAllFiles(isolate.getId());
while(itr.hasNext())
{
SourceFile m = (SourceFile) ((Map.Entry)itr.next()).getValue();
String name = m.getName();
int id = m.getId();
String path = m.getFullPath();
sb.append(id);
sb.append(' ');
sb.append(path);
sb.append(", "); //$NON-NLS-1$
sb.append(name);
sb.append(" ("); //$NON-NLS-1$
if (isolate.getId() == Isolate.DEFAULT_ID) {
sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
}
else {
HashMap<String, Object> wArgs = new HashMap<String, Object>();
wArgs.put("worker", isolate.getId() - 1); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("inWorker", wArgs)); //$NON-NLS-1$
}
sb.append(")"); //$NON-NLS-1$
sb.append(m_newline);
}
}
out( sb.toString() );
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noSourceFilesFound")); //$NON-NLS-1$
}
}
void doShowMemory()
{
out(uft());
}
void doShowLocations()
{
StringBuilder sb = new StringBuilder();
sb.append("Num Type Disp Enb Address What"+m_newline); //$NON-NLS-1$
// our list of breakpoints
int count = breakpointCount();
for(int i=0; i<count; i++)
{
BreakAction b = breakpointAt(i);
int num = b.getId();
FieldFormat.formatLong(sb, num, 3);
sb.append(" breakpoint "); //$NON-NLS-1$
if (b.isAutoDisable())
sb.append("dis "); //$NON-NLS-1$
else if (b.isAutoDelete())
sb.append("del "); //$NON-NLS-1$
else
sb.append("keep "); //$NON-NLS-1$
if (b.isEnabled())
sb.append("y "); //$NON-NLS-1$
else
sb.append("n "); //$NON-NLS-1$
Iterator<Location> itr = b.getLocations().iterator();
while(itr.hasNext())
{
Location l = itr.next();
SourceFile file = l.getFile();
String funcName = (file == null)
? getLocalizationManager().getLocalizedTextString("unknownBreakpointLocation") //$NON-NLS-1$
: file.getFunctionNameForLine(m_session, l.getLine()) ;
int offset = adjustOffsetForUnitTests((file == null) ? 0 : file.getOffsetForLine(l.getLine()));
sb.append("0x"); //$NON-NLS-1$
FieldFormat.formatLongToHex(sb, offset, 8);
sb.append(' ');
if (funcName != null)
{
Map<String, Object> funcArgs = new HashMap<String, Object>();
funcArgs.put("functionName", funcName); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("inFunctionAt", funcArgs)); //$NON-NLS-1$
}
sb.append(file.getName());
if (file != null)
{
sb.append("#"); //$NON-NLS-1$
sb.append(file.getId());
}
sb.append(':');
sb.append(l.getLine());
try
{
SwfInfo info = m_fileInfo.swfForFile(file, l.getIsolateId());
Map<String, Object> swfArgs = new HashMap<String, Object>();
swfArgs.put("swf", FileInfoCache.shortNameOfSwf(info)); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("inSwf", swfArgs)); //$NON-NLS-1$
if (l.getIsolateId() == Isolate.DEFAULT_ID) {
sb.append(" ("); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
sb.append(")"); //$NON-NLS-1$
}
else {
swfArgs = new HashMap<String, Object>();
swfArgs.put("worker", l.getIsolateId() - 1); //$NON-NLS-1$
sb.append(" ("); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("inWorker", swfArgs)); //$NON-NLS-1$
sb.append(")"); //$NON-NLS-1$
}
}
catch(NullPointerException npe)
{
// can't find the swf
sb.append(getLocalizationManager().getLocalizedTextString("nonRestorable")); //$NON-NLS-1$
}
sb.append(m_newline);
if (itr.hasNext())
sb.append(" "); //$NON-NLS-1$
}
}
out(sb.toString());
}
/**
* When running unit tests, we want byte offsets into the file to
* always be displayed as zero, so that the unit test expected
* results will match up with the actual results. This is just a
* simple helper function that deals with that.
*/
private int adjustOffsetForUnitTests(int offset)
{
if (System.getProperty("fdbunit")==null) //$NON-NLS-1$
return offset;
else
return 0;
}
void doShowDirectories()
{
out(getLocalizationManager().getLocalizedTextString("sourceDirectoriesSearched")); //$NON-NLS-1$
Iterator<String> iter = m_sourceDirectories.iterator();
while (iter.hasNext())
{
String dir = iter.next();
out(" " + dir); //$NON-NLS-1$
}
}
void doHalt() throws SuspendedException, NotConnectedException, NoResponseException
{
out(getLocalizationManager().getLocalizedTextString("attemptingToSuspend")); //$NON-NLS-1$
IsolateSession session = m_session.getWorkerSession(getActiveIsolateId());
if (!session.isSuspended())
session.suspend();
if (session.isSuspended())
out(getLocalizationManager().getLocalizedTextString("playerStopped")); //$NON-NLS-1$
else
out(getLocalizationManager().getLocalizedTextString("playerRunning")); //$NON-NLS-1$
}
public void appendReason(StringBuilder sb, int reason)
{
switch(reason)
{
case SuspendReason.Unknown:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_Unknown")); //$NON-NLS-1$
break;
case SuspendReason.Breakpoint:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_HitBreakpoint")); //$NON-NLS-1$
break;
case SuspendReason.Watch:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_HitWatchpoint")); //$NON-NLS-1$
break;
case SuspendReason.Fault:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_ProgramThrewException")); //$NON-NLS-1$
break;
case SuspendReason.StopRequest:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_StopRequest")); //$NON-NLS-1$
break;
case SuspendReason.Step:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_ProgramFinishedStepping")); //$NON-NLS-1$
break;
case SuspendReason.HaltOpcode:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_HaltOpcode")); //$NON-NLS-1$
break;
case SuspendReason.ScriptLoaded:
sb.append(getLocalizationManager().getLocalizedTextString("suspendReason_ScriptHasLoadedIntoFlashPlayer")); //$NON-NLS-1$
break;
}
}
/**
* The big ticket item, where all your questions are answered.
*
*/
void doInfo() throws AmbiguousException, PlayerDebugException
{
/* info without any args brings up help */
if (!hasMoreTokens())
out( getHelpTopic("info") ); //$NON-NLS-1$
else
{
/* otherwise we have a boatload of options */
String subCmdString = nextToken();
int subCmd = infoCommandFor(subCmdString);
switch(subCmd)
{
case INFO_ARGS_CMD:
doInfoArgs();
break;
case INFO_BREAK_CMD:
doInfoBreak();
break;
case INFO_FILES_CMD:
doInfoFiles();
break;
case INFO_FUNCTIONS_CMD:
doInfoFuncs();
break;
case INFO_HANDLE_CMD:
doInfoHandle();
break;
case INFO_LOCALS_CMD:
doInfoLocals();
break;
case INFO_SCOPECHAIN_CMD:
doInfoScopeChain();
break;
case INFO_SOURCES_CMD:
doInfoSources();
break;
case INFO_STACK_CMD:
doInfoStack();
break;
case INFO_VARIABLES_CMD:
doInfoVariables();
break;
case INFO_DISPLAY_CMD:
doInfoDisplay();
break;
case INFO_TARGETS_CMD:
doInfoTargets();
break;
case INFO_SWFS_CMD:
doInfoSwfs();
break;
case INFO_WORKERS_CMD:
doInfoWorkers();
break;
default:
doUnknown("info", subCmdString); //$NON-NLS-1$
break;
}
}
}
void doInfoWorkers() throws NotConnectedException, NotSupportedException, NotSuspendedException, NoResponseException
{
// waitTilHalted();
Isolate[] isolates = m_session.getWorkers();
if (isolates == null || isolates.length == 0) {
out(getLocalizationManager().getLocalizedTextString("noWorkersRunning")); //$NON-NLS-1$
return;
}
StringBuilder sb = new StringBuilder();
for (Isolate t : isolates) {
String status = getLocalizationManager().getLocalizedTextString("workerRunning"); //$NON-NLS-1$
if (m_session.getWorkerSession(t.getId()).isSuspended()) {
status = getLocalizationManager().getLocalizedTextString("workerSuspended"); //$NON-NLS-1$
}
if (m_activeIsolate == t.getId()) {
status += " " + getLocalizationManager().getLocalizedTextString("workerSelected"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (t.getId() == Isolate.DEFAULT_ID) {
sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
sb.append(" "); //$NON-NLS-1$
sb.append(Isolate.DEFAULT_ID - 1);
}
else {
HashMap<String, Object> workArgs = new HashMap<String, Object>();
workArgs.put("worker", (t.getId() - 1)); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("inWorker", workArgs)); //$NON-NLS-1$
}
sb.append(" - " + status + m_newline); //$NON-NLS-1$
}
out(sb.toString());
}
void doInfoStack() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
StringBuilder sb = new StringBuilder();
Frame[] stack = m_session.getWorkerSession(m_activeIsolate).getFrames();
if (stack == null || stack.length == 0)
sb.append(getLocalizationManager().getLocalizedTextString("noStackAvailable")); //$NON-NLS-1$
else
{
boolean showThis = propertyGet(INFO_STACK_SHOW_THIS) == 1;
for(int i=0; i<stack.length; i++)
{
// keep spitting out frames until we can't
Frame frame = stack[i];
boolean valid = appendFrameInfo(sb, frame, i, showThis, false);
sb.append(m_newline);
if (!valid)
break;
}
}
/* dump it out */
out(sb.toString());
}
/**
* Spit out frame information for a given frame number
*/
boolean appendFrameInfo(StringBuilder sb, Frame ctx, int frameNumber, boolean showThis, boolean showFileId) throws PlayerDebugException
{
boolean validFrame = true;
// some formatting properties
int i = frameNumber;
Location loc = ctx.getLocation();
SourceFile file = loc.getFile();
int line = loc.getLine();
String name = (file == null) ? "<null>" : file.getName(); //$NON-NLS-1$
String sig = ctx.getCallSignature();
String func = extractFunctionName(sig);
// file == null or line < 0 appears to be a terminator for stack info
if (file == null && line < 0)
{
validFrame = false;
}
else
{
Variable[] var = ctx.getArguments(m_session);
Variable dis = ctx.getThis(m_session);
boolean displayArgs = (func != null) || (var != null);
sb.append('#');
FieldFormat.formatLong(sb, i, 3);
sb.append(' ');
if (showThis && dis != null)
{
ExpressionCache.appendVariable(sb, dis, ctx.getIsolateId());
sb.append("."); //$NON-NLS-1$
}
if (func != null)
sb.append(func);
if (displayArgs)
{
sb.append('(');
for (int j=0; j<var.length; j++)
{
Variable v = var[j];
sb.append(v.getName());
sb.append('=');
ExpressionCache.appendVariableValue(sb, v.getValue(), ctx.getIsolateId());
if ((j+1)<var.length)
sb.append(", "); //$NON-NLS-1$
}
sb.append(")"); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("atFilename")); //$NON-NLS-1$
}
sb.append(name);
// if this file is currently being filtered put the source file id after it
if (file != null && (showFileId || !m_fileInfo.inFileList(file)))
{
sb.append('#');
sb.append( file.getId() );
}
sb.append(':');
sb.append(line);
}
return validFrame;
}
/** extract the function name from a signature */
public static String extractFunctionName(String sig)
{
// strip everything after the leading (
int at = sig.indexOf('(');
if (at > -1)
sig = sig.substring(0, at);
// trim the leading [object_name::] since it doesn't seem to add much
if (sig != null && (at = sig.indexOf("::")) > -1) //$NON-NLS-1$
sig = sig.substring(at+2);
return sig;
}
void doInfoVariables() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
// dump a set of locals
StringBuilder sb = new StringBuilder();
// use our expression cache formatting routine
try
{
Variable[] vars = m_session.getWorkerSession(m_activeIsolate).getVariableList();
for(int i=0; i<vars.length; i++)
{
Variable v = vars[i];
// all non-local and non-arg variables
if ( !v.isAttributeSet(VariableAttribute.IS_LOCAL) &&
!v.isAttributeSet(VariableAttribute.IS_ARGUMENT) )
{
ExpressionCache.appendVariable(sb, vars[i], m_activeIsolate);
sb.append(m_newline);
}
}
}
catch(NullPointerException npe)
{
sb.append(getLocalizationManager().getLocalizedTextString("noVariables")); //$NON-NLS-1$
}
out(sb.toString());
}
void doInfoDisplay()
{
StringBuilder sb = new StringBuilder();
sb.append("Num Enb Expression"+m_newline); //$NON-NLS-1$
// our list of displays
int count = displayCount();
for(int i=0; i<count; i++)
{
DisplayAction b = displayAt(i);
int num = b.getId();
String exp = b.getContent();
sb.append(':');
FieldFormat.formatLong(sb, num, 3);
if (b.isEnabled())
sb.append(" y "); //$NON-NLS-1$
else
sb.append(" n "); //$NON-NLS-1$
sb.append(exp);
sb.append(m_newline);
}
out(sb.toString());
}
void doInfoArgs() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
// dump a set of locals
StringBuilder sb = new StringBuilder();
// use our expression cache formatting routine
try
{
int num = propertyGet(DISPLAY_FRAME_NUMBER);
Frame[] frames = m_session.getWorkerSession(m_activeIsolate).getFrames();
Variable[] vars = frames[num].getArguments(m_session);
for(int i=0; i<vars.length; i++)
{
ExpressionCache.appendVariable(sb, vars[i], m_activeIsolate);
sb.append(m_newline);
}
}
catch(NullPointerException npe)
{
sb.append(getLocalizationManager().getLocalizedTextString("noArguments")); //$NON-NLS-1$
}
catch(ArrayIndexOutOfBoundsException aix)
{
sb.append(getLocalizationManager().getLocalizedTextString("notInValidFrame")); //$NON-NLS-1$
}
out(sb.toString());
}
void doInfoLocals() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
// dump a set of locals
StringBuilder sb = new StringBuilder();
// use our expression cache formatting routine
try
{
// get the variables from the requested frame
int num = propertyGet(DISPLAY_FRAME_NUMBER);
Frame[] ar = m_session.getWorkerSession(m_activeIsolate).getFrames();
Frame ctx = ar[num];
Variable[] vars = ctx.getLocals(m_session);
for(int i=0; i<vars.length; i++)
{
Variable v = vars[i];
// see if variable is local
if ( v.isAttributeSet(VariableAttribute.IS_LOCAL) )
{
ExpressionCache.appendVariable(sb, v, m_activeIsolate);
sb.append(m_newline);
}
}
}
catch(NullPointerException npe)
{
sb.append(getLocalizationManager().getLocalizedTextString("noLocals")); //$NON-NLS-1$
}
catch(ArrayIndexOutOfBoundsException aix)
{
sb.append(getLocalizationManager().getLocalizedTextString("notInValidFrame")); //$NON-NLS-1$
}
out(sb.toString());
}
void doInfoScopeChain() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
// dump the scope chain
StringBuilder sb = new StringBuilder();
// use our expression cache formatting routine
try
{
// get the scope chainfrom the requested frame
int num = propertyGet(DISPLAY_FRAME_NUMBER);
Frame[] ar = m_session.getWorkerSession(m_activeIsolate).getFrames();
Frame ctx = ar[num];
Variable[] scopes = ctx.getScopeChain(m_session);
for(int i=0; i<scopes.length; i++)
{
Variable scope = scopes[i];
ExpressionCache.appendVariable(sb, scope, m_activeIsolate);
sb.append(m_newline);
}
}
catch(NullPointerException npe)
{
sb.append(getLocalizationManager().getLocalizedTextString("noScopeChain")); //$NON-NLS-1$
}
catch(ArrayIndexOutOfBoundsException aix)
{
sb.append(getLocalizationManager().getLocalizedTextString("notInValidFrame")); //$NON-NLS-1$
}
out(sb.toString());
}
void doInfoTargets()
{
if (!haveConnection())
{
out(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
if (m_launchURI != null)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("uri", m_launchURI); //$NON-NLS-1$
out(getLocalizationManager().getLocalizedTextString("runWillLaunchUri", args)); //$NON-NLS-1$
}
}
else
{
String uri = m_session.getURI();
if (uri == null || uri.length() < 1)
err(getLocalizationManager().getLocalizedTextString("targetUnknown")); //$NON-NLS-1$
else
out(uri);
}
}
/**
* Dump some stats about our currently loaded swfs.
*/
void doInfoSwfs()
{
try
{
StringBuilder sb = new StringBuilder();
SwfInfo[] swfs = m_fileInfo.getSwfs(m_activeIsolate);
for(int i=0; i<swfs.length; i++)
{
SwfInfo e = swfs[i];
if (e == null || e.isUnloaded())
continue;
Map<String, Object> args = new HashMap<String, Object>();
args.put("swfName", FileInfoCache.nameOfSwf(e)); //$NON-NLS-1$
args.put("size", NumberFormat.getInstance().format(e.getSwfSize())); //$NON-NLS-1$
try
{
int size = e.getSwdSize(m_session);
// our swd is loaded so let's comb through our
// list of scripts and locate the range of ids.
SourceFile[] files = e.getSourceList(m_session);
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int j=0; j<files.length; j++)
{
SourceFile f = files[j];
int id = f.getId();
max = (id > max) ? id : max;
min = (id < min) ? id : min;
}
args.put("scriptCount", Integer.toString(e.getSourceCount(m_session))); //$NON-NLS-1$
args.put("min", Integer.toString(min)); //$NON-NLS-1$
args.put("max", Integer.toString(max)); //$NON-NLS-1$
args.put("plus", (e.isProcessingComplete()) ? "+" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
args.put("moreInfo", (size==0) ? getLocalizationManager().getLocalizedTextString("remainingSourceBeingLoaded") : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
catch(InProgressException ipe)
{
sb.append(getLocalizationManager().getLocalizedTextString("debugInfoBeingLoaded")); //$NON-NLS-1$
}
args.put("url", e.getUrl()); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("swfInfo", args)); //$NON-NLS-1$
sb.append(m_newline);
}
out( sb.toString() );
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noSWFs")); //$NON-NLS-1$
}
}
private static final int AUTHORED_FILE = 1; // a file that was created by the end user, e.g. MyApp.mxml
private static final int FRAMEWORK_FILE = 2; // a file from the Flex framework, e.g. mx.controls.Button.as, see FRAMEWORK_FILE_PACKAGES
private static final int SYNTHETIC_FILE = 3; // e.g. "<set up XML utilities.1>"
private static final int ACTIONS_FILE = 4; // e.g. "Actions for UIComponent: Frame 1 of Layer Name Layer 1"
private static final String[] FRAMEWORK_FILE_PACKAGES // package prefixes that we consider FRAMEWORK_FILEs
= new String[] {"mx","flex","text"}; // 'text' is Vellum (temporary) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
/**
* Given a file, guesses what type it is -- e.g. a file created by the end user,
* or a file from the Flex framework, etc.
*/
private int getFileType(SourceFile sourceFile)
{
String name = sourceFile.getName();
String pkg = sourceFile.getPackageName();
if (name.startsWith("<") && name.endsWith(">") || name.equals("GeneratedLocale")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return SYNTHETIC_FILE;
for (final String frameworkPkg : FRAMEWORK_FILE_PACKAGES )
{
// look for packages starting with pkgName
if (pkg.startsWith(frameworkPkg + '\\') ||
pkg.startsWith(frameworkPkg + '/') ||
pkg.equals(frameworkPkg))
{
return FRAMEWORK_FILE;
}
}
if (name.startsWith("Actions for")) //$NON-NLS-1$
return ACTIONS_FILE;
return AUTHORED_FILE;
}
void buildFileList(StringBuilder sb, boolean authoredFilesOnly)
{
SourceFile[] ar = m_fileInfo.getFileList(m_activeIsolate);
if (ar == null)
{
err(getLocalizationManager().getLocalizedTextString("noSourceFilesFound")); //$NON-NLS-1$
return;
}
Vector<String> authoredFiles = new Vector<String>();
Vector<String> frameworkFiles = new Vector<String>();
Vector<String> syntheticFiles = new Vector<String>();
Vector<String> actionsFiles = new Vector<String>();
for (int i = 0; i < ar.length; i++)
{
SourceFile m = ar[i];
int fileType = getFileType(m);
int id = m.getId();
// int fakeId = m_fileInfo.getFakeId(m);
String entry = m.getName() + "#" + id; //$NON-NLS-1$
switch (fileType)
{
case SYNTHETIC_FILE:
syntheticFiles.add(entry);
break;
case FRAMEWORK_FILE:
frameworkFiles.add(entry);
break;
case ACTIONS_FILE:
actionsFiles.add(entry);
break;
case AUTHORED_FILE:
authoredFiles.add(entry);
break;
}
}
int wrapAt = propertyGet(FILE_LIST_WRAP);
if (!authoredFilesOnly)
{
if (actionsFiles.size() > 0)
{
appendStrings(sb, actionsFiles, (actionsFiles.size() > wrapAt) );
}
if (frameworkFiles.size() > 0)
{
sb.append("---"+m_newline); //$NON-NLS-1$
appendStrings(sb, frameworkFiles, (frameworkFiles.size() > wrapAt) );
}
if (syntheticFiles.size() > 0)
{
sb.append("---"+m_newline); //$NON-NLS-1$
appendStrings(sb, syntheticFiles, (syntheticFiles.size() > wrapAt) );
}
sb.append("---"+m_newline); //$NON-NLS-1$
}
appendStrings(sb, authoredFiles, (authoredFiles.size() > wrapAt) );
}
/**
* Dump a list of strings contained a vector
* If flow is set then the strings are placed
* on a single line and wrapped at $columnwidth
*/
void appendStrings(StringBuilder sb, Vector<String> v, boolean flow)
{
int count = v.size();
int width = 0;
int maxCol = propertyGet(COLUMN_WIDTH);
for (int i = 0; i < count; i++)
{
String s = v.get(i);
sb.append(s);
// too many of them, then wrap according to columnwidth
if (flow)
{
width += (s.length() + 2);
if (width >= maxCol)
{
sb.append(m_newline);
width = 0;
}
else
sb.append(", "); //$NON-NLS-1$
}
else
sb.append(m_newline);
}
// add a line feed for flow based
if (flow && width > 0)
sb.append(m_newline);
}
void doInfoFiles()
{
try
{
StringBuilder sb = new StringBuilder();
if (hasMoreTokens())
{
String arg = nextToken();
listFilesMatching(sb, arg);
}
else
{
buildFileList(sb, false);
}
out(sb.toString());
}
catch(NullPointerException npe)
{
throw new IllegalStateException();
}
}
public void waitForMetaData() throws InProgressException
{
// perform a query to see if our metadata has loaded
int metadatatries = propertyGet(METADATA_ATTEMPTS);
int maxPerCall = 8; // cap on how many attempt we make per call
int tries = Math.min(maxPerCall, metadatatries);
if (tries > 0)
{
int remain = metadatatries - tries; // assume all get used up
// perform the call and then update our remaining number of attempts
try
{
tries = waitForMetaData(tries);
remain = metadatatries - tries; // update our used count
}
catch(InProgressException ipe)
{
propertyPut(METADATA_ATTEMPTS, remain);
throw ipe;
}
}
}
/**
* Wait for the API to load function names, which
* exist in the form of external meta-data.
*
* Only do this tries times, then give up
*
* We wait period * attempts
*/
public int waitForMetaData(int attempts) throws InProgressException
{
int start = attempts;
int period = propertyGet(METADATA_ATTEMPTS_PERIOD);
while(attempts > 0)
{
// are we done yet?
if (isMetaDataAvailable())
break;
else
try { attempts--; Thread.sleep(period); } catch(InterruptedException ie) {}
}
// throw exception if still not ready
if (!isMetaDataAvailable())
throw new InProgressException();
return start-attempts; // remaining number of tries
}
/**
* Ask each swf if metadata processing is complete
*/
public boolean isMetaDataAvailable()
{
boolean allLoaded = true;
try
{
// we need to ask the session since our fileinfocache will hide the exception
SwfInfo[] swfs = m_session.getSwfs();
for(int i=0; i<swfs.length; i++)
{
// check if our processing is finished.
SwfInfo swf = swfs[i];
if (swf != null && !swf.isProcessingComplete())
{
allLoaded = false;
break;
}
}
}
catch(NoResponseException nre)
{
// ok we still need to wait for player to read the swd in
allLoaded = false;
}
// count the number of times we checked and it wasn't there
if (!allLoaded)
{
int count = propertyGet(METADATA_NOT_AVAILABLE);
count++;
propertyPut(METADATA_NOT_AVAILABLE, count);
}
else
{
// success so we reset our attempt counter
propertyPut(METADATA_ATTEMPTS, METADATA_RETRIES);
}
return allLoaded;
}
void doInfoHandle()
{
if (hasMoreTokens())
{
// user specified a fault
String faultName = nextToken();
// make sure we know about this one
if (!m_faultTable.exists(faultName))
err(getLocalizationManager().getLocalizedTextString("unrecognizedFault")); //$NON-NLS-1$
else
listFault(faultName);
}
else
{
// dump them all
StringBuilder sb = new StringBuilder();
appendFaultTitles(sb);
Object names[] = m_faultTable.names();
Arrays.sort(names);
for(int i=0; i<names.length; i++)
appendFault(sb, (String)names[i]);
out ( sb.toString() );
}
}
void doInfoFuncs()
{
StringBuilder sb = new StringBuilder();
String arg = null;
// we take an optional single arg which specifies a module
try
{
// let's wait a bit for the background load to complete
waitForMetaData();
if (hasMoreTokens())
{
arg = nextToken();
int id = arg.equals(".") ? propertyGet(LIST_MODULE) : parseFileArg(-1, arg); //$NON-NLS-1$
SourceFile m = m_fileInfo.getFile(id, m_activeIsolate);
listFunctionsFor(sb, m);
}
else
{
SourceFile[] ar = m_fileInfo.getFileList(m_activeIsolate);
if (ar == null)
err(getLocalizationManager().getLocalizedTextString("noSourceFilesFound")); //$NON-NLS-1$
else
{
for(int i = 0; ar != null && i < ar.length; i++)
{
SourceFile m = ar[i];
listFunctionsFor(sb, m);
}
}
}
out(sb.toString());
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noFunctionsFound")); //$NON-NLS-1$
}
catch(ParseException pe)
{
err(pe.getMessage());
}
catch(NoMatchException nme)
{
err(nme.getMessage());
}
catch(AmbiguousException ae)
{
err(ae.getMessage());
}
catch(InProgressException ipe)
{
err(getLocalizationManager().getLocalizedTextString("functionListBeingPrepared")); //$NON-NLS-1$
}
}
void listFunctionsFor(StringBuilder sb, SourceFile m)
{
String[] names = m.getFunctionNames(m_session);
if (names == null)
return;
Arrays.sort(names);
Map<String, Object> args = new HashMap<String, Object>();
args.put("sourceFile", m.getName() + "#" + m.getId()); //$NON-NLS-1$ //$NON-NLS-2$
sb.append(getLocalizationManager().getLocalizedTextString("functionsInSourceFile", args)); //$NON-NLS-1$
sb.append(m_newline);
for (int j = 0; j < names.length; j++)
{
String fname = names[j];
sb.append(' ');
sb.append(fname);
sb.append(' ');
sb.append(m.getLineForFunctionName(m_session, fname));
sb.append(m_newline);
}
}
void listFilesMatching(StringBuilder sb, String match)
{
SourceFile[] sourceFiles = m_fileInfo.getFiles(match);
for (int j = 0; j < sourceFiles.length; j++)
{
SourceFile sourceFile = sourceFiles[j];
sb.append(sourceFile.getName());
sb.append('#');
sb.append(sourceFile.getId());
sb.append(m_newline);
}
}
void doInfoSources()
{
try
{
StringBuilder sb = new StringBuilder();
buildFileList(sb, true);
out(sb.toString());
}
catch(NullPointerException npe)
{
throw new IllegalStateException();
}
}
void doInfoBreak() throws NotConnectedException
{
// waitTilHalted();
StringBuilder sb = new StringBuilder();
sb.append("Num Type Disp Enb Address What"+m_newline); //$NON-NLS-1$
int count = breakpointCount();
for(int i=0; i<count; i++)
{
BreakAction b = breakpointAt(i);
int status = b.getStatus();
boolean isResolved = (status == BreakAction.RESOLVED);
Location l = b.getLocation();
SourceFile file = (l != null) ? l.getFile() : null;
String funcName = (file == null) ? null : file.getFunctionNameForLine(m_session, l.getLine()) ;
boolean singleSwf = b.isSingleSwf();
int cmdCount = b.getCommandCount();
int hits = b.getHits();
String cond = b.getConditionString();
boolean silent = b.isSilent();
int offset = adjustOffsetForUnitTests((file == null) ? 0 : file.getOffsetForLine(l.getLine()));
int num = b.getId();
FieldFormat.formatLong(sb, num, 3);
sb.append(" breakpoint "); //$NON-NLS-1$
if (b.isAutoDisable())
sb.append("dis "); //$NON-NLS-1$
else if (b.isAutoDelete())
sb.append("del "); //$NON-NLS-1$
else
sb.append("keep "); //$NON-NLS-1$
if (b.isEnabled())
sb.append("y "); //$NON-NLS-1$
else
sb.append("n "); //$NON-NLS-1$
sb.append("0x"); //$NON-NLS-1$
FieldFormat.formatLongToHex(sb, offset, 8);
sb.append(' ');
if (funcName != null)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("functionName", funcName); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("inFunctionAt", args)); //$NON-NLS-1$
}
if (file != null)
{
sb.append(file.getName());
if (isResolved && singleSwf)
{
sb.append("#"); //$NON-NLS-1$
sb.append(file.getId());
}
sb.append(':');
sb.append(l.getLine());
}
else
{
String expr = b.getBreakpointExpression();
if (expr != null)
sb.append(expr);
}
switch (status)
{
case BreakAction.UNRESOLVED:
sb.append(getLocalizationManager().getLocalizedTextString("breakpointNotYetResolved")); //$NON-NLS-1$
break;
case BreakAction.AMBIGUOUS:
sb.append(getLocalizationManager().getLocalizedTextString("breakpointAmbiguous")); //$NON-NLS-1$
break;
case BreakAction.NOCODE:
sb.append(getLocalizationManager().getLocalizedTextString("breakpointNoCode")); //$NON-NLS-1$
break;
}
// if a single swf break action then append more info
if (singleSwf && isResolved)
{
try
{
SwfInfo info = m_fileInfo.swfForFile(file, l.getIsolateId());
Map<String, Object> swfArgs = new HashMap<String, Object>();
swfArgs.put("swf", FileInfoCache.nameOfSwf(info)); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("inSwf", swfArgs)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
// can't find the swf
sb.append(getLocalizationManager().getLocalizedTextString("nonRestorable")); //$NON-NLS-1$
}
}
sb.append(m_newline);
final String INDENT = " "; //$NON-NLS-1$
// state our condition if we have one
if (cond != null && cond.length() > 0)
{
sb.append(INDENT);
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointCondition", cond ); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString(getLocalizationManager().getLocalizedTextString("stopOnlyIfConditionMet", args))); //$NON-NLS-1$
sb.append(m_newline);
}
// now if its been hit, lets state the fact
if (hits > 0)
{
sb.append(INDENT);
Map<String, Object> args = new HashMap<String, Object>();
args.put("count", Integer.toString(hits)); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("breakpointAlreadyHit", args)); //$NON-NLS-1$
sb.append(m_newline);
}
// silent?
if (silent)
{
sb.append(INDENT);
sb.append(getLocalizationManager().getLocalizedTextString("silentBreakpoint")+m_newline); //$NON-NLS-1$
}
// now if any commands are trailing then we pump them out
for(int j=0; j<cmdCount; j++)
{
sb.append(INDENT);
sb.append(b.commandAt(j));
sb.append(m_newline);
}
}
// }
int wcount = watchpointCount();
for(int k = 0; k < wcount; k++)
{
WatchAction b = watchpointAt(k);
int id = b.getId();
FieldFormat.formatLong(sb, id, 4);
int flags = b.getKind();
switch(flags)
{
case WatchKind.READ:
sb.append("rd watchpoint "); //$NON-NLS-1$
break;
case WatchKind.WRITE:
sb.append("wr watchpoint "); //$NON-NLS-1$
break;
case WatchKind.READWRITE:
default:
sb.append("watchpoint "); //$NON-NLS-1$
break;
}
sb.append("keep "); //$NON-NLS-1$
sb.append("y "); //$NON-NLS-1$
sb.append(" "); //$NON-NLS-1$
sb.append(b.getExpr());
sb.append(m_newline);
}
int ccount = catchpointCount();
for (int k = 0; k < ccount; k++)
{
CatchAction c = catchpointAt(k);
int id = c.getId();
FieldFormat.formatLong(sb, id, 4);
String typeToCatch = c.getTypeToCatch();
if (typeToCatch == null)
typeToCatch = "*"; //$NON-NLS-1$
sb.append("catch "); //$NON-NLS-1$
sb.append("keep "); //$NON-NLS-1$
sb.append("y "); //$NON-NLS-1$
sb.append(" "); //$NON-NLS-1$
sb.append(typeToCatch);
sb.append(m_newline);
}
out(sb.toString());
}
/**
* Dump out the state of the execution, either the fact we are running
* or the breakpoint we hit.
*/
void dumpHaltState(boolean postStep) throws NotConnectedException
{
// spit out any event output, if we are to resume after a fault and we're not stepping then we're done.
processEvents();
// System.out.println("processEvents = "+m_requestResume);
//if (m_requestResume && !postStep)
if (hasAnyPendingResumes() != -1 && !postStep)
return;
if (!m_session.isConnected())
{
// session is kaput
out(getLocalizationManager().getLocalizedTextString("sessionTerminated")); //$NON-NLS-1$
exitSession();
}
else
{
if (hasAnythingSuspended())
{
// capture our break location / information
StringBuilder sbLine = new StringBuilder();
dumpBreakLine(postStep, sbLine);
// Process our breakpoints.
// Since we can have conditional breakpoints, which the
// player always breaks for, but we may not want to, the variable
// m_requestResume may be set after this call. Additionally,
// silent may be set for one of two reasons; 1) m_requestResume
// was set to true in the call or one or more breakpoints that
// hit contained the keyword silent in their command list.
//
StringBuilder sbBreak = new StringBuilder();
boolean silent = processBreak(postStep, sbBreak, m_activeIsolate);
StringBuilder sb = new StringBuilder();
if (silent)
{
// silent means we only spit out our current location
dumpBreakLine(postStep, sb);
}
else
{
// not silent means we append things like normal
sb.append(sbLine);
if (sbLine.length() > 0 && sbLine.charAt(sbLine.length()-1) != '\n')
sb.append(m_newline);
sb.append(sbBreak);
}
// output whatever was generated
if (sb.length() > 0)
out( sb.toString() );
// System.out.println("processbreak = "+m_requestResume+",silent="+silent+",reason="+m_session.suspendReason());
}
else
{
// very bad, set stepping so that we don't trigger a continue on a breakpoint or fault
out(getLocalizationManager().getLocalizedTextString("playerDidNotStop")); //$NON-NLS-1$
}
}
}
Location getCurrentLocation()
{
return getCurrentLocationIsolate(Isolate.DEFAULT_ID);
}
Location getCurrentLocationIsolate(int isolateId)
{
Location where = null;
try
{
Frame[] ar = m_session.getWorkerSession(isolateId).getFrames();
propertyPut(CURRENT_FRAME_DEPTH, (ar.length > 0) ? ar.length : 0);
where = ( (ar.length > 0) ? ar[0].getLocation() : null);
}
catch(PlayerDebugException pde)
{
// where == null
}
return where;
}
void dumpBreakLine(boolean postStep, StringBuilder sb) throws NotConnectedException
{
int bp = -1;
String name = getLocalizationManager().getLocalizedTextString("unknownFilename"); //$NON-NLS-1$
int line = -1;
// clear our current frame display
propertyPut(DISPLAY_FRAME_NUMBER, 0);
int targetIsolate = getLastStoppedIsolate();
boolean activeIsolateChanged = (m_activeIsolate != targetIsolate);
m_activeIsolate = targetIsolate;
propertyPut(LIST_WORKER, targetIsolate);
/* dump a context line to the console */
Location l = getCurrentLocationIsolate(targetIsolate);
// figure out why we stopped
int reason = SuspendReason.Unknown;
try { reason = m_session.getWorkerSession(targetIsolate).suspendReason(); } catch(PlayerDebugException pde) {}
// then see if it because of a swfloaded event
if( reason == SuspendReason.ScriptLoaded)
{
// since the player takes a long time to provide swf/swd, try 80 * 250ms = ~20s
if (propertyGet(METADATA_ATTEMPTS) > 0)
try { waitForMetaData(80); } catch(InProgressException ipe) { }
m_fileInfo.setDirty();
m_fileInfo.getSwfsIsolate(targetIsolate);
//propertyPut(LIST_MODULE, m_fileInfo.getFakeId(m_fileInfo.getFile(1, targetIsolate)));
processEvents();
propagateBreakpoints(targetIsolate);
propertyPut(LIST_LINE, 1);
propertyPut(LIST_WORKER, targetIsolate);
propertyPut(LIST_MODULE, 1);
sb.append(getLocalizationManager().getLocalizedTextString("additionalCodeLoaded")); //$NON-NLS-1$
if ( activeIsolateChanged ) {
sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged")+ " " + (targetIsolate - 1) + m_newline); //$NON-NLS-1$ //$NON-NLS-2$
}
sb.append(m_newline);
if (resolveBreakpoints(sb))
sb.append(getLocalizationManager().getLocalizedTextString("setAdditionalBreakpoints")+m_newline); //$NON-NLS-1$
else
sb.append(getLocalizationManager().getLocalizedTextString("fixBreakpoints")+m_newline); //$NON-NLS-1$
setPromptState(InitialPromptState.SHOWN_ONCE, targetIsolate);
}
else if ( l == null || l.getFile() == null )
{
if ( activeIsolateChanged ) {
sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged")+ " " + (targetIsolate - 1) + m_newline);
}
// no idea where we are ?!?
propertyPut(LAST_FRAME_DEPTH, 0);
sb.append(getLocalizationManager().getLocalizedTextString("executionHalted")); //$NON-NLS-1$
sb.append(' ');
/** disable this line (and enable the one after) if implementation Extensions are not provided */
appendBreakInfo(sb, m_activeIsolate);
//sb.append("unknown location");
}
else
{
if ( activeIsolateChanged ) {
sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged")+ " " + (targetIsolate - 1) + m_newline);
}
SourceFile file = l.getFile();
name = file.getName();
line = l.getLine();
String funcName = file.getFunctionNameForLine(m_session, line) ;
// where were we last time
int lastModule = propertyGet(LIST_MODULE);
int lastDepth = propertyGet(LAST_FRAME_DEPTH);
int thisModule = file.getId();
int thisDepth = propertyGet(CURRENT_FRAME_DEPTH); // triggered via getCurrentLocation()
// mark where we stopped
propertyPut(LAST_FRAME_DEPTH, thisDepth);
// if we have changed our context or we are not spitting out source then dump our location
if (!postStep || lastModule != thisModule || lastDepth != thisDepth )
{
// is it a fault?
String reasonForHalting;
if ( reason == SuspendReason.Fault || reason == SuspendReason.StopRequest)
{
StringBuilder s = new StringBuilder();
appendReason(s, reason);
reasonForHalting = s.toString();
}
// if its a breakpoint add that information
else if ( (bp = enabledBreakpointIndexOf(l)) > -1 )
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(breakpointAt(bp).getId())); //$NON-NLS-1$
reasonForHalting = getLocalizationManager().getLocalizedTextString("hitBreakpoint", args); //$NON-NLS-1$
}
else
{
reasonForHalting = getLocalizationManager().getLocalizedTextString("executionHalted"); //$NON-NLS-1$
}
Map<String, Object> args = new HashMap<String, Object>();
args.put("reasonForHalting", reasonForHalting); //$NON-NLS-1$
args.put("fileAndLine", name + ':' + line); //$NON-NLS-1$
String formatString;
if (funcName != null)
{
args.put("functionName", funcName); //$NON-NLS-1$
formatString = "haltedInFunction"; //$NON-NLS-1$
}
else
{
formatString = "haltedInFile"; //$NON-NLS-1$
}
sb.append(getLocalizationManager().getLocalizedTextString(formatString, args));
if (!m_fullnameOption)
sb.append(m_newline);
}
// set current listing poistion and emit emacs trigger
setListingPosition(thisModule, line, targetIsolate);
// dump our source line if not in emacs mode
if (!m_fullnameOption)
appendSource(sb, file.getId(), line, file.getLine(line), false);
}
}
private int getLastStoppedIsolate() {
int targetIsolate = Isolate.DEFAULT_ID;
if (m_breakIsolates.size() > 0) {
targetIsolate = m_breakIsolates.get(m_breakIsolates.size() - 1);
}
return targetIsolate;
}
void appendFullnamePosition(StringBuilder sb, SourceFile file, int lineNbr)
{
// fullname option means we dump 'path:line:col?:offset', which is used for emacs !
String name = file.getFullPath();
if (name.startsWith("file:/")) //$NON-NLS-1$
name = name.substring(6);
// Ctrl-Z Ctrl-Z
sb.append('\u001a');
sb.append('\u001a');
sb.append(name);
sb.append(':');
sb.append(lineNbr);
sb.append(':');
sb.append('0');
sb.append(':');
sb.append("beg"); //$NON-NLS-1$
sb.append(':');
sb.append('0');
}
// pretty print a trace statement to the console
void dumpTraceLine(String s)
{
StringBuilder sb = new StringBuilder();
sb.append("[trace] "); //$NON-NLS-1$
sb.append(s);
out(sb.toString());
}
// pretty print a fault statement to the console
void dumpFaultLine(FaultEvent e)
{
StringBuilder sb = new StringBuilder();
// use a slightly different format for ConsoleErrorFaults
if (e instanceof ConsoleErrorFault)
{
sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenDisplayingConsoleError")); //$NON-NLS-1$
sb.append(' ');
sb.append(e.information);
}
else
{
String name = e.name();
sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenDisplayingFault")); //$NON-NLS-1$
sb.append(' ');
sb.append(name);
if (e.information != null && e.information.length() > 0)
{
sb.append(getLocalizationManager().getLocalizedTextString("informationAboutFault")); //$NON-NLS-1$
sb.append(e.information);
}
}
out( sb.toString() );
}
/**
* Called when a swf has been loaded by the player
* @param e event documenting the load
*/
void handleSwfLoadedEvent(SwfLoadedEvent e)
{
// first we dump out a message that displays we have loaded a swf
dumpSwfLoadedLine(e);
}
// pretty print a SwfLoaded statement to the console
void dumpSwfLoadedLine(SwfLoadedEvent e)
{
// now rip off any trailing ? options
int at = e.path.lastIndexOf('?');
String name = (at > -1) ? e.path.substring(0, at) : e.path;
StringBuilder sb = new StringBuilder();
sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenSwfLoaded")); //$NON-NLS-1$
sb.append(' ');
sb.append(name);
sb.append(" - "); //$NON-NLS-1$
Map<String, Object> args = new HashMap<String, Object>();
args.put("size", NumberFormat.getInstance().format(e.swfSize)); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("sizeAfterDecompression", args)); //$NON-NLS-1$
out(sb.toString());
}
/**
* Propagate current breakpoints to the newly loaded swf.
*/
void propagateBreakpoints(int isolateId) throws NotConnectedException
{
// get the newly added swf, which lands at the end list
SwfInfo[] swfs = m_fileInfo.getSwfsIsolate(isolateId);
SwfInfo swf = (swfs.length > 1) ? swfs[swfs.length-1] : null;
// now walk through all breakpoints propagating the
// the break for each source and line number we
// find in the new swf
int size = m_breakpoints.size();
for (int i = 0; (swf != null) && i < size; i++)
{
// dont do this for single swf breakpoints
BreakAction bp = breakpointAt(i);
if (bp.isSingleSwf())
continue;
if (bp.getStatus() != BreakAction.RESOLVED)
continue;
try
{
Location l = bp.getLocation();
int line = l.getLine();
SourceFile f = l.getFile();
Location newLoc = findAndEnableBreak(swf, f, line);
if (newLoc != null)
bp.addLocation(newLoc);
}
catch(InProgressException ipe)
{
if (breakpointCount() > 0)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(bp.getId())); //$NON-NLS-1$
out(getLocalizationManager().getLocalizedTextString("breakpointNotPropagated", args)); //$NON-NLS-1$
}
}
}
}
/**
* Perform the tasks need for when a swf is unloaded
* the player
*/
void handleSwfUnloadedEvent(SwfUnloadedEvent e)
{
// print out the notification
dumpSwfUnloadedLine(e);
}
// pretty print a SwfUnloaded statement to the console
void dumpSwfUnloadedLine(SwfUnloadedEvent e)
{
// now rip off any trailing ? options
int at = e.path.lastIndexOf('?');
String name = (at > -1) ? e.path.substring(0, at) : e.path;
StringBuilder sb = new StringBuilder();
sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenSwfUnloaded")); //$NON-NLS-1$
sb.append(' ');
sb.append(name);
out(sb.toString());
}
void doContinue() throws NotConnectedException
{
//int stoppedIsolate = getLastStoppedIsolate();
int stoppedIsolate = m_activeIsolate;
waitTilHalted(stoppedIsolate);
// this will trigger a resume when we get back to the main loop
//m_requestResume = true;
setRequestResume(true, stoppedIsolate);
m_repeatLine = m_currentLine;
}
boolean hasAnythingSuspended() throws NotConnectedException {
boolean hasAnythingSuspended = false;
for (Integer id : m_breakIsolates) {
if (m_session.getWorkerSession(id).isSuspended()) {
hasAnythingSuspended = true;
break;
}
}
return hasAnythingSuspended;
}
int hasAnyPendingResumes() throws NotConnectedException {
int rid = -1;
if (m_mainState.m_requestResume)
return Isolate.DEFAULT_ID;
for (Integer id : m_breakIsolates) {
if (getIsolateState(id).m_requestResume) {
rid = id;
break;
}
}
return rid;
}
/**
* Returns the first isolate's id for which we need to keep showing prompts.
*
* @return The isolate id
* @throws NotConnectedException
*/
int hasPendingInitialPrompts() throws NotConnectedException {
int rid = -1;
for (Integer id : m_breakIsolates) {
if (getPromptState(id) != InitialPromptState.DONE) {
rid = id;
break;
}
}
return rid;
}
/**
* Our main loop when the player is off running
*/
public void runningLoop() throws NoResponseException, NotConnectedException
{
int update = propertyGet(UPDATE_DELAY);
boolean nowait = (propertyGet(NO_WAITING) == 1) ? true : false; // DEBUG ONLY; do not document
boolean stop = false;
boolean noConnection = !haveConnection();
boolean hasAnythingSuspended = false;
int targetIsolate = Isolate.DEFAULT_ID;
if (!noConnection) {
hasAnythingSuspended = hasAnythingSuspended();
}
if (hasAnythingSuspended) {
if (m_breakIsolates.size() > 0) {
targetIsolate = m_breakIsolates.get(m_breakIsolates.size() - 1);
}
}
// not there, not connected or already halted and no pending resume requests => we are done
//if (noConnection || (hasAnythingSuspended && !m_requestResume) )
if (noConnection || (hasAnythingSuspended && hasAnyPendingResumes() == -1) )
{
processEvents();
stop = true;
if(!noConnection) {
/** At this point, some isolate is in suspended state and will be until a resume
* is requested via the prompt. We thus check for any pending prompts to be displayed
* for freshly loaded swfs. If any, we switch to that worker and prompt the user to set
* any break points. */
int pendingPromptIsolate = -1;
if ( m_lastPromptIsolate != -1 && ( getPromptState(m_lastPromptIsolate) != InitialPromptState.DONE ) ) {
pendingPromptIsolate = m_lastPromptIsolate;
} else {
pendingPromptIsolate = hasPendingInitialPrompts();
}
if ( pendingPromptIsolate != -1) {
dumpInitialPrompt(pendingPromptIsolate);
}
}
}
while(!stop)
{
// allow keyboard input
if (!nowait)
m_keyboardReadRequest = true;
int pendingResumeId = hasAnyPendingResumes();
if (pendingResumeId != -1)
{
// resume execution (request fulfilled) and look for keyboard input
try
{
IsolateSession workerSession = m_session.getWorkerSession(pendingResumeId);
//if (m_stepResume)
if (getStepResume(pendingResumeId))
workerSession.stepContinue();
else {
workerSession.resume();
}
/** The user is done setting initial breakpoints for this isolate,
* clear any pending initial prompts */
setPromptState(InitialPromptState.DONE, pendingResumeId);
removeBreakIsolate(pendingResumeId);
}
catch(NotSuspendedException nse)
{
err(getLocalizationManager().getLocalizedTextString("playerAlreadyRunning")); //$NON-NLS-1$
}
setRequestResume(false, pendingResumeId);
setRequestHalt(false, pendingResumeId);
setStepResume(false, pendingResumeId);
// m_requestResume = false;
// m_requestHalt = false;
// m_stepResume = false;
}
// sleep for a bit, then process our events.
try { Thread.sleep(update); } catch(InterruptedException ie) {}
processEvents();
// lost connection?
if (!haveConnection())
{
stop = true;
dumpHaltState(false);
}
else if (hasAnythingSuspended())
{
/**
* We have stopped for some reason. Now for all cases, but conditional
* breakpoints, we should be done. For conditional breakpoints it
* may be that the condition has turned out to be false and thus
* we need to continue
*/
/**
* Now before we do this see, if we have a valid break reason, since
* we could be still receiving incoming messages, even though we have halted.
* This is definately the case with loading of multiple SWFs. After the load
* we get info on the swf.
*/
if (m_breakIsolates.size() > 0) {
targetIsolate = m_breakIsolates.get(m_breakIsolates.size() - 1);
}
else {
targetIsolate = Isolate.DEFAULT_ID;
}
int tries = 3;
IsolateSession workerSession = m_session.getWorkerSession(targetIsolate);
while (tries-- > 0 && workerSession.suspendReason() == SuspendReason.Unknown)
try { Thread.sleep(100); processEvents(); } catch(InterruptedException ie) {}
dumpHaltState(false);
//if (!m_requestResume)
if (!getRequestResume(targetIsolate))
stop = true;
}
else if (nowait)
{
stop = true; // for DEBUG only
}
else
{
/**
* We are still running which is fine. But let's see if the user has
* tried to enter something on the keyboard. If so, then we need to
* stop
*/
if (!m_keyboardInput.isEmpty() && System.getProperty("fdbunit")==null) //$NON-NLS-1$
{
// flush the queue and prompt the user if they want us to halt
m_keyboardInput.clear();
try
{
if (yesNoQuery(getLocalizationManager().getLocalizedTextString("doYouWantToHalt"))) //$NON-NLS-1$
{
out(getLocalizationManager().getLocalizedTextString("attemptingToHalt")); //$NON-NLS-1$
IsolateSession workerSession = m_session.getWorkerSession(m_activeIsolate);
workerSession.suspend();
// m_session.suspend();
getIsolateState(m_activeIsolate).m_requestHalt = true;
// no connection => dump state and end
if (!haveConnection())
{
dumpHaltState(false);
stop = true;
}
else if (!workerSession.isSuspended())
err(getLocalizationManager().getLocalizedTextString("couldNotHalt")); //$NON-NLS-1$
}
}
catch(IllegalArgumentException iae)
{
out(getLocalizationManager().getLocalizedTextString("escapingFromDebuggerPendingLoop")); //$NON-NLS-1$
propertyPut(NO_WAITING, 1);
stop = true;
}
catch(IOException io)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("error", io.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("continuingDueToError", args)); //$NON-NLS-1$
}
catch(SuspendedException se)
{
// lucky us, already stopped
}
}
}
// System.out.println("doContinue resume="+m_requestResume+",isSuspended="+m_session.isSuspended());
}
// DEBUG ONLY: if we are not waiting then process some events
if (nowait)
processEvents();
}
/**
* Does a few of things -
* 1) Sets the target worker as 'active'.
* 2) Propagates any breakpoints already set which are relevant to the target worker.
* 3) Outputs messages indicating additional code load and worker switch.
* 4) Sets {@link DebugCLIIsolateState#m_promptState} as {@link InitialPromptState#SHOWN_ONCE} if
* the prompt hasn't been shown for this worker before.
*/
private void dumpInitialPrompt(int targetIsolate) throws NotConnectedException{
boolean activeIsolateChanged = ( m_activeIsolate != targetIsolate );
m_activeIsolate = targetIsolate;
if ( activeIsolateChanged ) {
propertyPut(LIST_WORKER, targetIsolate);
propertyPut(LIST_LINE, 1);
propertyPut(LIST_MODULE, 1);
propagateBreakpoints(targetIsolate);
}
StringBuilder sb = new StringBuilder();
if (getPromptState(targetIsolate) == InitialPromptState.NEVER_SHOWN) {
sb.append(getLocalizationManager().getLocalizedTextString("additionalCodeLoaded")); //$NON-NLS-1$
sb.append(m_newline + getLocalizationManager().getLocalizedTextString("workerChanged")+ " " + (targetIsolate - 1) + m_newline); //$NON-NLS-1$ //$NON-NLS-2$
sb.append(m_newline);
setPromptState(InitialPromptState.SHOWN_ONCE, targetIsolate);
}
if (resolveBreakpoints(sb))
sb.append(getLocalizationManager().getLocalizedTextString("setAdditionalBreakpoints")+m_newline); //$NON-NLS-1$
else
sb.append(getLocalizationManager().getLocalizedTextString("fixBreakpoints")+m_newline); //$NON-NLS-1$
// output whatever has to be
if (sb.length() > 0)
out( sb.toString() );
}
private void removeBreakIsolate(int targetIsolate) {
for (int i = 0; i < m_breakIsolates.size();i++) {
int id = m_breakIsolates.get(i);
if (id == targetIsolate) {
m_breakIsolates.remove(i);
break;
}
}
}
/**
* Bring the listing location back to the current frame
*/
void doHome()
{
try
{
Location l = getCurrentLocationIsolate(m_activeIsolate);
SourceFile file = l.getFile();
int module = file.getId();
int line = l.getLine();
int worker = l.getIsolateId();
// now set it
setListingPosition(module, line, worker);
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("currentLocationUnknown")); //$NON-NLS-1$
}
}
// Dump a source line of text to the display
void dumpStep() throws NotConnectedException
{
dumpHaltState(true);
}
/**
* Simple interface used with stepWithTimeout(). Implementors of this interface
* are expected to call one of these function: Session.stepInto(), Session.stepOver(),
* Session.stepOut(), or Session.stepContinue().
*/
private interface AnyKindOfStep
{
public void step() throws PlayerDebugException;
}
/**
* Helper function to do a stepInto, stepOver, stepOut, or stepContinue,
* and then to block (processing events) until either the step has completed
* or it has timed out.
*/
private void stepWithTimeout(AnyKindOfStep step, int isolateId) throws PlayerDebugException
{
int timeout = m_session.getPreference(SessionManager.PREF_RESPONSE_TIMEOUT);
long timeoutTime = System.currentTimeMillis() + timeout;
step.step();
IsolateSession workerSession = m_session.getWorkerSession(isolateId);
while (System.currentTimeMillis() < timeoutTime && !workerSession.isSuspended())
{
processEvents();
if (!m_session.isSuspended())
{
try { Thread.sleep(1); } catch (InterruptedException e) { }
}
}
if (System.currentTimeMillis() >= timeoutTime && !workerSession.isSuspended())
throw new NoResponseException(timeout);
}
private boolean allowedToStep(int isolateId) throws NotConnectedException
{
int suspendReason = m_session.getWorkerSession(isolateId).suspendReason();
if (suspendReason == SuspendReason.ScriptLoaded)
{
err(getLocalizationManager().getLocalizedTextString("cannotStep")); //$NON-NLS-1$
return false;
}
return true;
}
/**
* Perform step into, optional COUNT parameter
*/
void doStep() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
if (!allowedToStep(m_activeIsolate))
return;
int count = 1;
if (hasMoreTokens())
count = nextIntToken();
DebugCLIIsolateState state = getIsolateState(m_activeIsolate);
while(count-- > 0)
{
stepWithTimeout(new AnyKindOfStep() {
public void step() throws PlayerDebugException
{
m_session.getWorkerSession(m_activeIsolate).stepInto();
}
}, m_activeIsolate);
for (;;)
{
dumpStep();
if (state.m_requestResume) // perhaps we hit a conditional breakpoint
{
state.m_requestResume = false;
stepWithTimeout(new AnyKindOfStep() {
public void step() throws PlayerDebugException
{
m_session.getWorkerSession(m_activeIsolate).stepContinue();
}
}, m_activeIsolate);
}
else
{
break;
}
}
}
m_repeatLine = m_currentLine;
}
/**
* Perform step over, optional COUNT parameter
*/
void doNext() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
if (!allowedToStep(m_activeIsolate))
return;
int count = 1;
if (hasMoreTokens())
count = nextIntToken();
DebugCLIIsolateState state = getIsolateState(m_activeIsolate);
try
{
while(count-- > 0)
{
stepWithTimeout(new AnyKindOfStep() {
public void step() throws PlayerDebugException
{
m_session.getWorkerSession(m_activeIsolate).stepOver();
}
},m_activeIsolate);
for (;;)
{
dumpStep();
if (state.m_requestResume) // perhaps we hit a conditional breakpoint
{
state.m_requestResume = false;
stepWithTimeout(new AnyKindOfStep() {
public void step() throws PlayerDebugException
{
m_session.getWorkerSession(m_activeIsolate).stepContinue();
}
},m_activeIsolate);
}
else
{
break;
}
}
}
}
catch(NoResponseException nre)
{
if (count > 0)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("count", Integer.toString(count)); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("abortingStep", args)); //$NON-NLS-1$
}
}
m_repeatLine = m_currentLine;
}
/**
* Perform step out
*/
void doFinish() throws PlayerDebugException
{
waitTilHalted(m_activeIsolate);
if (!allowedToStep(m_activeIsolate))
return;
DebugCLIIsolateState state = getIsolateState(m_activeIsolate);
try
{
// make sure we have another frame?
int depth = propertyGet(CURRENT_FRAME_DEPTH);
if (depth < 2)
err(getLocalizationManager().getLocalizedTextString("finishCommandNotMeaningfulOnOutermostFrame")); //$NON-NLS-1$
else
{
stepWithTimeout(new AnyKindOfStep() {
public void step() throws PlayerDebugException
{
m_session.getWorkerSession(m_activeIsolate).stepOut();
}
},m_activeIsolate);
for (;;)
{
dumpStep();
if (state.m_requestResume) // perhaps we hit a conditional breakpoint
{
state.m_requestResume = false;
stepWithTimeout(new AnyKindOfStep() {
public void step() throws PlayerDebugException
{
m_session.getWorkerSession(m_activeIsolate).stepContinue();
}
},m_activeIsolate);
}
else
{
break;
}
}
m_repeatLine = m_currentLine;
}
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("finishCommandNotMeaningfulWithoutStack")); //$NON-NLS-1$
}
}
/**
* Delete a breakpoint, very similar logic to disable.
*/
void doDelete() throws IOException, AmbiguousException, NotConnectedException
{
waitTilHalted(m_activeIsolate);
try
{
if (!hasMoreTokens())
{
// no args means delete all breakpoints, last chance...
if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askDeleteAllBreakpoints"))) //$NON-NLS-1$
{
int count = breakpointCount();
for(int i=count-1; i>-1; i--)
removeBreakpointAt(i);
removeAllWatchpoints();
removeAllCatchpoints();
}
}
else
{
// optionally specify 'display' or 'breakpoint'
String arg = nextToken();
int cmd = disableCommandFor(arg);
int id = -1;
if (cmd == CMD_DISPLAY)
doUnDisplay();
else
{
if (cmd == CMD_BREAK)
id = nextIntToken(); // ignore and get next number token
else
id = Integer.parseInt(arg);
do
{
try
{
int at = breakpointIndexOf(id);
if(at > -1)
{
removeBreakpointAt(at);
}
else
{
at = watchpointIndexOf(id);
if (at > -1)
{
removeWatchpointAt(at);
}
else
{
at = catchpointIndexOf(id);
removeCatchpointAt(at);
}
}
}
catch(IndexOutOfBoundsException iob)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
}
if (hasMoreTokens())
id = nextIntToken();
else
id = -1;
// keep going till we're blue in the face; also note that we cache'd a copy of locations
// so that breakpoint numbers are consistent.
}
while( id > -1 );
}
}
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
}
}
/**
* Set a breakpoint
*/
void doBreak() throws NotConnectedException
{
int isolateId = propertyGet(LIST_WORKER);
/* wait a bit if we are not halted */
waitTilHalted(isolateId);
int module = propertyGet(LIST_MODULE);
int line = propertyGet(LIST_LINE);
String arg = null;
/* currentXXX may NOT be invalid! */
try
{
if (hasMoreTokens())
{
arg = nextToken();
int[] result = parseLocationArg(module, line, arg, isolateId);
module = result[0];
line = result[1];
}
else
{
// no parameter mean use current location; null pointer if nothing works
Location l = getCurrentLocationIsolate(isolateId);
SourceFile file = l.getFile();
module = file.getId();
line = l.getLine();
}
// // check to see if there are any existing breakpoints at this file/line
// LinkedList existingBreakpoints = new LinkedList();
// int start = 0;
// for (;;)
// {
// int bp = breakpointIndexOf(module, line, start, true);
// if (bp == -1)
// break; // no more matches
// boolean isEnabled = breakpointAt(bp).isEnabled();
// existingBreakpoints.add("" + bp + (isEnabled ? "" : " (disabled)"));
// }
// if (existingBreakpoints.size() > 0)
// {
// String
// }
// go off; create it and set it
BreakAction b = addBreakpoint(module, line, isolateId); // throws npe if not able to set
Location l = b.getLocation();
int which = b.getId();
String name = l.getFile().getName();
int offset = adjustOffsetForUnitTests(l.getFile().getOffsetForLine(line));
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(which)); //$NON-NLS-1$
args.put("file", name); //$NON-NLS-1$
args.put("line", Integer.toString(line)); //$NON-NLS-1$
String formatString;
if (offset != 0)
{
args.put("offset", "0x" + Integer.toHexString(offset)); //$NON-NLS-1$ //$NON-NLS-2$
formatString = "createdBreakpointWithOffset"; //$NON-NLS-1$
}
else
{
formatString = "createdBreakpoint"; //$NON-NLS-1$
}
out(getLocalizationManager().getLocalizedTextString(formatString, args));
// worked so add it to our tracking state
propertyPut(BPNUM, which);
}
catch(ParseException pe)
{
err(pe.getMessage());
}
catch(AmbiguousException ae)
{
err(ae.getMessage());
}
catch(NoMatchException nme)
{
// We couldn't find a function name or filename which matched what
// the user entered. Do *not* fail; instead, just save this breakpoint
// away, and later, as more ABCs get loaded from the SWF, we may be
// able to resolve this breakpoint.
BreakAction b = addUnresolvedBreakpoint(arg, m_activeIsolate);
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(b.getId())); //$NON-NLS-1$
out(getLocalizationManager().getLocalizedTextString("breakpointCreatedButNotYetResolved", args)); //$NON-NLS-1$
// add it to our tracking state
propertyPut(BPNUM, b.getId());
}
catch(NullPointerException npe)
{
String filename;
try
{
filename = m_fileInfo.getFile(module, isolateId).getName() + "#" + module; //$NON-NLS-1$
}
catch (Exception e)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("fileNumber", Integer.toString(module)); //$NON-NLS-1$
filename = getLocalizationManager().getLocalizedTextString("fileNumber", args); //$NON-NLS-1$
}
Map<String, Object> args = new HashMap<String, Object>();
args.put("filename", filename); //$NON-NLS-1$
args.put("line", Integer.toString(line)); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("breakpointNotSetNoCode", args)); //$NON-NLS-1$
}
}
/**
* Clear a breakpoint
*/
void doClear() throws NotConnectedException
{
int module = propertyGet(LIST_MODULE);
int line = propertyGet(LIST_LINE);
int isolateId = propertyGet(LIST_WORKER);
String arg = null;
/* wait a bit if we are not halted */
waitTilHalted(m_activeIsolate);
/* currentXXX may NOT be invalid! */
try
{
if (hasMoreTokens())
{
arg = nextToken();
int[] result = parseLocationArg(module, line, arg, isolateId);
module = result[0];
line = result[1];
}
// map the breakpoint to location and then delete it
removeBreakpoint(module, line);
}
catch(ParseException pe)
{
err(pe.getMessage());
}
catch(NoMatchException nme)
{
if (removeUnresolvedBreakpoint(arg) == null)
err(getLocalizationManager().getLocalizedTextString("breakpointLocationUnknown")); //$NON-NLS-1$
}
catch(AmbiguousException ae)
{
err(ae.getMessage());
}
catch(ArrayIndexOutOfBoundsException aio)
{
// means no breakpoint at this location
err(getLocalizationManager().getLocalizedTextString("breakpointLocationUnknown")); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("breakpointNotCleared")); //$NON-NLS-1$
}
}
/**
* Remove the breakpoint from our table and then determine
*/
BreakAction removeBreakpoint(int fileId, int line) throws ArrayIndexOutOfBoundsException, NotConnectedException
{
int at = breakpointIndexOf(fileId, line);
return removeBreakpointAt(at);
}
BreakAction removeUnresolvedBreakpoint(String unresolvedLocation) throws NotConnectedException
{
int size = breakpointCount();
for(int i=0; i<size; i++)
{
BreakAction b = breakpointAt(i);
String s = b.getBreakpointExpression();
if (s != null && s.equals(unresolvedLocation))
return removeBreakpointAt(i);
}
return null;
}
BreakAction removeBreakpointAt(int at) throws ArrayIndexOutOfBoundsException, NotConnectedException
{
BreakAction a = breakpointAt(at);
// if (isolateId == Isolate.DEFAULT_ID)
m_breakpoints.removeElementAt(at);
// else
// getIsolateState(isolateId).m_breakpoints.removeElementAt(at);
if (a.getStatus() == BreakAction.RESOLVED)
breakDisableRequest(a.getLocations());
return a;
}
/**
* Attempt to create new breakpoint at the given file and line. It will be set
* @param fileId source file identifier
* @param line line number
* @return object associated with breakpoint
*/
BreakAction addBreakpoint(int fileId, int line, int isolateId) throws NotConnectedException, NullPointerException
{
// use fileId SourceFile to denote the name of file in which we wish to set a breakpoint
SourceFile f = m_fileInfo.getFile(fileId, isolateId);
LocationCollection col = enableBreak(f, line, isolateId);
BreakAction b = new BreakAction(col); // throws NullPointerException if collection is null
b.setEnabled(true);
b.setSingleSwf(m_fileInfo.isSwfFilterOn());
breakpointAdd(b);
return b;
}
/**
* Create a new, *unresolved* breakpoint. Unresolved means we weren't able to
* parse the location string, presumably because the filename to which it refers
* has not yet been loaded.
* @param unresolvedLocation the breakpoint location, exactly as typed by the user
* @return object associated with breakpoint
*/
private BreakAction addUnresolvedBreakpoint(String unresolvedLocation, int isolateId)
{
BreakAction b = new BreakAction(unresolvedLocation);
b.setEnabled(true);
b.setSingleSwf(m_fileInfo.isSwfFilterOn());
breakpointAdd(b);
return b;
}
/**
* Try to resolve any breakpoints which have not yet been resolved. We
* do this every time a new ABC or SWF is loaded. NOTE: The return
* value does NOT indicate whether any breakpoints were resolved! Rather,
* it indicates whether the operation was considered "successful."
* If a previously-unresolved breakpoint is now ambiguous, then that is
* an error, and the return value is 'false' (indicating that the
* debugger should halt).
*/
private boolean resolveBreakpoints(StringBuilder sb)
{
int count = breakpointCount();
boolean success = true;
for (int i=0; i<count; ++i)
{
BreakAction b = breakpointAt(i);
try
{
tryResolveBreakpoint(b, sb);
}
catch (Exception e) // AmbiguousException or NullPointerException
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(b.getId())); //$NON-NLS-1$
args.put("expression", b.getBreakpointExpression()); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("attemptingToResolve", args)); //$NON-NLS-1$
sb.append(m_newline);
sb.append(e.getMessage());
sb.append(m_newline);
success = false;
}
}
return success;
}
/**
* Try to resolve one breakpoint. We do this every time a new ABC or
* SWF is loaded.
* @param b the breakpoint to resolve (it's okay if it's already resolved)
* @param sb a StringBuilder to which any messages for are appended;
* to the user.
* @return true if the breakpoint is resolved
* @throws AmbiguousException
* @throws NullPointerException
*/
private boolean tryResolveBreakpoint(BreakAction b, StringBuilder sb) throws AmbiguousException
{
int status = b.getStatus();
boolean resolved = (status == BreakAction.RESOLVED);
if (status == BreakAction.UNRESOLVED) // we don't do anything for RESOLVED or AMBIGUOUS
{
/* wait a bit if we are not halted */
try
{
waitTilHalted(m_activeIsolate);
// First we check for the case where this breakpoint already has a
// filename and line number, because those were determined during a
// previous session, but then the user did a "kill".
//
// If this fails, then the "else" clause deals with the case where
// the user typed in an expression for which we have not yet found
// a filename and line number.
if (enableBreakpoint(b, b.isAutoDisable(), b.isAutoDelete()))
{
resolved = true;
}
else
{
int module = propertyGet(LIST_MODULE);
int line = propertyGet(LIST_LINE);
int isolateId = propertyGet(LIST_WORKER);
String arg = b.getBreakpointExpression();
if (arg != null)
{
int[] result = parseLocationArg(module, line, arg, isolateId);
// whoo-hoo, it resolved!
module = result[0];
line = result[1];
// use module SourceFile to denote the name of file in which we wish to set a breakpoint
SourceFile f = m_fileInfo.getFile(module, isolateId);
// SourceFile f = m_fileInfo.getFakeFile(module);
LocationCollection col = enableBreak(f, line, isolateId);
if (col.isEmpty())
throw new NullPointerException(getLocalizationManager().getLocalizedTextString("noExecutableCode")); //$NON-NLS-1$
b.setLocations(col);
Location l = col.first();
SourceFile file = (l != null) ? l.getFile() : null;
String funcName = (file == null) ? null : file.getFunctionNameForLine(m_session, l.getLine()) ;
Map<String, Object> args = new HashMap<String, Object>();
String formatString;
args.put("breakpointNumber", Integer.toString(b.getId())); //$NON-NLS-1$
String filename = file.getName();
if (b.isSingleSwf() && file != null)
{
filename = filename + "#" + file.getId(); //$NON-NLS-1$
}
args.put("file", filename); //$NON-NLS-1$
args.put("line", new Integer(l.getLine())); //$NON-NLS-1$
if (funcName != null)
{
args.put("functionName", funcName); //$NON-NLS-1$
formatString = "resolvedBreakpointToFunction"; //$NON-NLS-1$
}
else
{
formatString = "resolvedBreakpointToFile"; //$NON-NLS-1$
}
sb.append(getLocalizationManager().getLocalizedTextString(formatString, args));
sb.append(m_newline);
sb.append(m_newline);
resolved = true;
}
}
}
catch (NotConnectedException e)
{
// Ignore
}
catch (NoMatchException e)
{
// Okay, it's still not resolved; do nothing
}
catch (ParseException e)
{
// this shouldn't happen
if (Trace.error)
Trace.trace(e.toString());
}
catch (AmbiguousException e)
{
b.setStatus(BreakAction.AMBIGUOUS);
throw e; // rethrow
}
catch (NullPointerException e)
{
b.setStatus(BreakAction.NOCODE);
throw e; // rethrow
}
}
return resolved;
}
/**
* Enable a breakpoint using the SourceFile as a template
* for the source file in which the breakpoint should be
* set.
*/
LocationCollection enableBreak(SourceFile f, int line, int isolateId) throws NotConnectedException
{
LocationCollection col = new LocationCollection();
boolean singleSwfBreakpoint = m_fileInfo.isSwfFilterOn();
SwfInfo swf = m_fileInfo.getSwfFilter();
// If we have a swf filter enabled then we only want to
// set a breakpoint in a specific swf not all of them
try
{
if (singleSwfBreakpoint)
{
Location l = findAndEnableBreak(swf, f, line);
col.add(l);
}
else
{
// walk all swfs looking to add this breakpoint
SwfInfo[] swfs = m_fileInfo.getSwfs(isolateId);
for(int i=0; i<swfs.length; i++)
{
swf = swfs[i];
if (swf != null)
{
Location l = findAndEnableBreak(swf, f, line);
if (l != null)
col.add(l);
}
}
}
}
catch(InProgressException ipe)
{
if (Trace.error)
Trace.trace( ( (swf==null)?"SWF ":swf.getUrl() )+" still loading, breakpoint at "+f.getName()+":"+line+" not set"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
return col;
}
/**
* Enable a breakpoint for a particular swf if the sourceFile
* is available in that swf.
* @param swf if null, then set the breakpoint in the given source file
* otherwise try to locate a matching source file in given swf.
* @return null if the swf does not contain this source file
*/
Location findAndEnableBreak(SwfInfo swf, SourceFile f, int line) throws NotConnectedException, InProgressException
{
int fileId = f.getId();
if (swf != null)
{
SourceFile sameFile = m_fileInfo.similarFileInSwf(swf, f);
if (sameFile != null)
fileId = sameFile.getId();
else
fileId = -1;
}
Location l = (fileId > -1) ? breakEnableRequest(fileId, line, swf.getIsolateId()) : null;
return l;
}
/**
* Received when a breakpoint has been removed (or disabled)
*/
Location breakEnableRequest(int fileId, int line, int isolateId) throws NotConnectedException
{
Location l = null;
try
{
l = m_session.getWorkerSession(isolateId).setBreakpoint(fileId, line);
}
catch(NoResponseException nre)
{
/**
* This could be that we have an old player which does not
* respond to this request, or that we have a new player and
* the location was not set.
*/
}
return l;
}
/**
* Notification that a breakpoint has been removed (or disabled)
* at the CLI level and we may need to remove it at the session level
*/
void breakDisableRequest(LocationCollection col) throws NotConnectedException
{
// now let's comb the table looking to see if this breakpoint should
// be removed at the session level. Use the first entry as a template
// for which location we are talking about.
int at = 0;
boolean hit = false;
Location l = col.first();
do
{
at = breakpointIndexOf(l, at);
if (at > -1)
{
if (breakpointAt(at).isEnabled())
hit = true;
else
at++; // our location match is not enabled but let's continue after the hit
}
}
while(at > -1 && !hit);
// no one matches, so let's remove it at the session level
if (!hit)
{
Iterator<Location> itr = col.iterator();
while(itr.hasNext())
{
l = itr.next();
try { m_session.clearBreakpoint(l); } catch(NoResponseException nre) {}
}
}
}
BreakAction breakpointAt(int at) {
// if (isolateId == Isolate.DEFAULT_ID)
return m_breakpoints.elementAt(at);
// else
// return getIsolateState(isolateId).m_breakpoints.elementAt(at);
}
boolean breakpointAdd(BreakAction a) {
// if (isolateId == Isolate.DEFAULT_ID)
return m_breakpoints.add(a);
// else
// return getIsolateState(isolateId).m_breakpoints.add(a);
}
int breakpointCount() {
// if (isolateId == Isolate.DEFAULT_ID)
return m_breakpoints.size();
// else
// return getIsolateState(isolateId).m_breakpoints.size();
}
/**
* Probe the table looking for the first breakpoint that
* matches our criteria. Various permutations of the call are supported.
*/
int breakpointIndexOf(int fileId, int line) {
return breakpointIndexOf(fileId, line, 0, true);
}
int breakpointIndexOf(Location l, int start) {
return breakpointIndexOf(l.getFile().getId(), l.getLine(), start, true);
}
int enabledBreakpointIndexOf(Location l) {
return breakpointIndexOf(l.getFile().getId(), l.getLine(), 0, false);
}
int breakpointIndexOf(int fileId, int line, int start, boolean includeDisabled)
{
int size = breakpointCount();
int hit = -1;
for(int i=start; (hit<0) && (i<size) ; i++)
{
BreakAction b = breakpointAt(i);
if (b.locationMatches(fileId, line) && (includeDisabled || b.isEnabled()) )
hit = i;
}
return hit;
}
// probe by identifier
int breakpointIndexOf(int id)
{
int size = breakpointCount();
int hit = -1;
for(int i=0; (hit<0) && (i<size) ; i++)
{
BreakAction b = breakpointAt(i);
if (b.getId() == id)
hit = i;
}
return hit;
}
// access to display
DisplayAction displayAt(int at) { return m_displays.get(at); }
boolean displayAdd(DisplayAction a) { return m_displays.add(a); }
void displayRemoveAt(int at) { m_displays.remove(at); }
int displayCount() { return m_displays.size(); }
// probe by id
int displayIndexOf(int id)
{
int size = displayCount();
int hit = -1;
for(int i=0; (hit<0) && (i<size) ; i++)
{
DisplayAction b = displayAt(i);
if (b.getId() == id)
hit = i;
}
return hit;
}
void doSet() throws NotConnectedException
{
/* wait a bit if we are not halted */
// waitTilHalted();
try
{
ValueExp exp = null;
if (!hasMoreTokens())
err(getLocalizationManager().getLocalizedTextString("setCommand")); //$NON-NLS-1$
else
{
// pull the expression
String s = restOfLine();
// parse and eval which causes the assignment to occur...
if ( (exp = parseExpression(s)) == null )
; // failed parse
// make sure contains assignment
else if ( !exp.containsAssignment() )
throw new IllegalAccessException("="); //$NON-NLS-1$
else
evalExpression(exp, m_activeIsolate);
}
}
catch(IllegalAccessException iae)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("operator", iae.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("missingOperator", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
}
}
void doPrint() throws NotConnectedException
{
/* wait a bit if we are not halted */
// waitTilHalted();
try
{
Object result = null;
boolean isLookupMembers = false;
if (!hasMoreTokens())
{
try
{
// attempt to get the last result
result = m_exprCache.get("$"); //$NON-NLS-1$
}
catch(ArrayIndexOutOfBoundsException aib)
{
err(getLocalizationManager().getLocalizedTextString("commandHistoryIsEmpty")); //$NON-NLS-1$
throw new NullPointerException();
}
}
else
{
// pull the rest of the line
String s = restOfLine();
// first parse it, then attempt to evaluate the expression
ValueExp expr = parseExpression(s);
// make sure no assignment
if ( expr.containsAssignment() )
throw new IllegalAccessException();
result = evalExpression(expr, m_activeIsolate).value;
isLookupMembers = expr.isLookupMembers();
}
/* it worked, add it to the list */
int which = m_exprCache.add(result);
/* dump the output */
StringBuilder sb = new StringBuilder();
sb.append('$');
sb.append(which);
sb.append(" = "); //$NON-NLS-1$
if (result instanceof Variable)
result = ((Variable)result).getValue();
if (result instanceof InternalProperty)
sb.append( ((InternalProperty)result).valueOf() );
else if (isLookupMembers)
sb.append(result);
else
ExpressionCache.appendVariableValue(sb, result, m_activeIsolate);
out( sb.toString() );
m_repeatLine = m_currentLine;
}
catch(ArrayIndexOutOfBoundsException aio)
{
// $n not in range 0..size
Map<String, Object> args = new HashMap<String, Object>();
args.put("number", aio.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("historyHasNotReached", args)); //$NON-NLS-1$
}
catch(IllegalAccessException iae)
{
err(getLocalizationManager().getLocalizedTextString("noSideEffectsAllowed")); //$NON-NLS-1$
}
catch(NoSuchVariableException nsv)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("variable", nsv.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
}
}
/* parse the given string and produce an error message as appropriate */
ValueExp parseExpression(String s)
{
ValueExp expr = null;
try
{
expr = m_exprCache.parse(s);
}
catch(ParseException pe)
{
// bad operation code
err(getLocalizationManager().getLocalizedTextString("expressionCouldNotBeParsed") + " " + pe.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
}
catch(IOException io)
{
// thrown from parser
err(getLocalizationManager().getLocalizedTextString("expressionCouldNotBeParsed") + " " + s); //$NON-NLS-1$ //$NON-NLS-2$
}
return expr;
}
/*
* Evaluate the given expression
*/
EvaluationResult evalExpression(ValueExp expr, int isolateId) { return evalExpression(expr, true, isolateId); }
EvaluationResult evalExpression(ValueExp expr, boolean displayExceptions, int isolateId)
{
/* now we go off and evaluate the expression */
EvaluationResult result = null;
try
{
result = m_exprCache.evaluate(expr, isolateId);
}
catch(NoSuchVariableException nsv)
{
if (displayExceptions)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("variable", nsv.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
}
}
catch(NumberFormatException nfe)
{
if (displayExceptions)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("value", nfe.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("couldNotConvertToNumber", args)); //$NON-NLS-1$
}
}
catch(PlayerFaultException pfe)
{
if (displayExceptions)
err(pfe.getMessage());
}
catch (PlayerDebugException e)
{
if (displayExceptions)
err(e.getMessage());
}
return result;
}
/**
* Specialized dump of the contents of a movie clip tree, dumping
* all the _target properties of all MCs
* @throws NoResponseException
* @throws NotSuspendedException
*/
void doMcTree() throws NotConnectedException, NotSuspendedException, NoResponseException
{
/* wait a bit if we are not halted */
waitTilHalted(m_activeIsolate);
try
{
String var = nextToken(); // our variable reference
String member = "_target"; //$NON-NLS-1$
boolean printPath = false;
Object result = null;
String name = null;
// did the user specify a member name
if (hasMoreTokens())
{
member = nextToken();
// did they specify some other options
while(hasMoreTokens())
{
String option = nextToken();
if (option.equalsIgnoreCase("fullpath")) //$NON-NLS-1$
printPath = true;
}
}
// first parse it, then attempt to evaluate the expression
ValueExp expr = parseExpression(var);
result = evalExpression(expr, m_activeIsolate).value;
StringBuilder sb = new StringBuilder();
if (result instanceof Variable)
{
name = ((Variable)result).getName();
result = ((Variable)result).getValue();
}
// It worked an should now be a value that we can traverse looking for member properties
if (result instanceof Value)
{
ArrayList<Object> e = new ArrayList<Object>();
dumpTree(new HashMap<Object, String>(), e, name, (Value)result, member);
// now sort according to our criteria
treeResults(sb, e, member, printPath);
}
else
throw new NoSuchVariableException(result);
out( sb.toString() );
}
catch(NoSuchVariableException nsv)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("variable", nsv.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
}
}
/**
* Set the context from which info files
* and all other file releated commands
* will operate from.
*
* It no swf is given then we use the
* default mode which is to display all
* files from all swfs. Files with identical
* names are only displayed once.
*/
void doViewSwf()
{
try
{
if (hasMoreTokens())
{
String swfName = nextToken();
Map<String, Object> args = new HashMap<String, Object>();
args.put("swf", swfName); //$NON-NLS-1$
if (m_fileInfo.setSwfFilter(swfName))
{
out(getLocalizationManager().getLocalizedTextString("commandsLimitedToSpecifiedSwf", args)); //$NON-NLS-1$
}
else
{
err(getLocalizationManager().getLocalizedTextString("notValidSwf", args)); //$NON-NLS-1$
}
}
else
{
m_fileInfo.setSwfFilter(null);
out(getLocalizationManager().getLocalizedTextString("commandsApplyToAllSwfs")); //$NON-NLS-1$
}
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
}
}
/**
* Increment the frame context by 1 and display the new current frame.
*/
void doUp() throws PlayerDebugException
{
int num = propertyGet(DISPLAY_FRAME_NUMBER) + 1;
try
{
propertyPut(DISPLAY_FRAME_NUMBER, num);
dumpFrame(num);
setListingToFrame(num);
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
}
catch(ArrayIndexOutOfBoundsException aie)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("frameNumber", Integer.toString(num)); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("frameDoesNotExist", args)); //$NON-NLS-1$
}
m_repeatLine = m_currentLine;
}
/**
* Decrement the frame context by 1 and display the new current frame.
*/
void doDown() throws PlayerDebugException
{
int num = propertyGet(DISPLAY_FRAME_NUMBER) - 1;
try
{
propertyPut(DISPLAY_FRAME_NUMBER, num);
dumpFrame(num);
setListingToFrame(num);
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
}
catch(ArrayIndexOutOfBoundsException aie)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("frameNumber", Integer.toString(num)); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("frameDoesNotExist", args)); //$NON-NLS-1$
}
m_repeatLine = m_currentLine;
}
/**
* Set the frame context to the given number and display the new current frame.
*/
void doFrame() throws PlayerDebugException
{
int num = 0; // frame 0 by default
try
{
if (hasMoreTokens())
num = nextIntToken();
propertyPut(DISPLAY_FRAME_NUMBER, num);
dumpFrame(num);
setListingToFrame(num);
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("notANumber", args)); //$NON-NLS-1$
}
catch(ArrayIndexOutOfBoundsException aie)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("frameNumber", Integer.toString(num)); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("frameDoesNotExist", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noActiveSession")); //$NON-NLS-1$
}
}
// Displays information on the current frame
// @throws ArrayIndexOutOfBoundsException if frame 'frm' doesn't exist
void dumpFrame(int frm) throws PlayerDebugException, ArrayIndexOutOfBoundsException
{
StringBuilder sb = new StringBuilder();
Frame[] ar = m_session.getFrames();
appendFrameInfo(sb, ar[frm], frm, false, true);
sb.append(m_newline);
out(sb.toString());
}
// set the listing command to point to the file/line of the given frame
void setListingToFrame(int frameNum) throws PlayerDebugException
{
// set the module and line
Frame[] frames = m_session.getFrames();
Frame ctx = frames[frameNum];
Location l = ctx.getLocation();
SourceFile f = l.getFile();
int id = f.getId();
int line = l.getLine();
setListingPosition(id, line, ctx.getIsolateId());
}
// Set teh listing position to change to the given module and line number
// also triggers emacs to move to this position if enabled
void setListingPosition(int module, int line, int workerid)
{
propertyPut(LIST_MODULE, module);
propertyPut(LIST_LINE, line);
propertyPut(LIST_WORKER, workerid);
// if we are running under emacs then dump out our new location
if (m_fullnameOption)
{
SourceFile f = m_fileInfo.getFile(module);
if (f != null)
{
StringBuilder sb = new StringBuilder();
appendFullnamePosition(sb, f, line);
sb.append('\n'); // not sure why this is needed but it seems to address some emacs bugs
out(sb.toString());
}
}
}
/**
* Traverse the given variables dumping any Movieclips we find that
* contain a member called 'member'
* @throws NotConnectedException
* @throws NoResponseException
* @throws NotSuspendedException
*/
void dumpTree(Map<Object, String> tree, List<Object> e, String name, Value result, String member) throws NotSuspendedException, NoResponseException, NotConnectedException
{
// name for this variable
if (name == null)
name = ""; //$NON-NLS-1$
// have we seen it already
if (tree.containsKey(result))
return;
tree.put(result, name); // place it
// first iterate over our members looking for 'member'
Value proto = result;
boolean done = false;
while(!done && proto != null)
{
Variable[] members = proto.getMembers(m_session);
proto = null;
// see if we find one called 'member'
for(int i=0; i<members.length; i++)
{
Variable m = members[i];
String memName = m.getName();
if (memName.equals(member) && !tree.containsKey(m))
{
e.add(name);
e.add(result);
e.add(m);
tree.put(m, name+"."+memName); //$NON-NLS-1$
done = true;
}
else if (memName.equals("__proto__")) //$NON-NLS-1$
proto = members[i].getValue();
}
}
// now traverse other mcs recursively
done = false;
proto = result;
while(!done && proto != null)
{
Variable[] members = proto.getMembers(m_session);
proto = null;
// see if we find an mc
for(int i=0; i<members.length; i++)
{
Variable m = members[i];
String memName = m.getName();
// if our type is NOT object or movieclip then we are done
if (m.getValue().getType() != VariableType.OBJECT && m.getValue().getType() != VariableType.MOVIECLIP)
;
else if (m.getValue().getId() != Value.UNKNOWN_ID)
dumpTree(tree, e, name, m.getValue(), member);
else if (memName.equals("__proto__")) //$NON-NLS-1$
{
proto = m.getValue();
// name = name + ".__proto__";
}
}
}
}
StringBuilder treeResults(StringBuilder sb, List<Object> e, String memName, boolean fullName)
{
// walk the list
Iterator<Object> i = e.iterator();
while(i.hasNext())
{
String name = (String) i.next();
Variable key = (Variable) i.next();
Variable val = (Variable)i.next();
// sb.append(key);
// sb.append(".");
// sb.append(val.getName());
if (fullName)
sb.append(name);
ExpressionCache.appendVariableValue(sb, key.getValue(), key.getName(), key.getIsolateId());
sb.append("."); //$NON-NLS-1$
sb.append(memName);
sb.append(" = "); //$NON-NLS-1$
ExpressionCache.appendVariableValue(sb, val.getValue(), val.getName(), val.getIsolateId());
sb.append(m_newline);
}
return sb;
}
/**
* Output a source line of code to the output channel formatting nicely
*/
public void outputSource(int module, int line, String s)
{
StringBuilder sb = new StringBuilder();
appendSource(sb, module, line, s, true);
out( sb.toString() );
}
void appendSource(StringBuilder sb, int module, int line, String s, boolean markCurrent)
{
String lineS = String.valueOf(line);
int padding = 6 - lineS.length();
// if we are the current location then mark it
if (markCurrent && isCurrentLocation(module, line))
sb.append('=');
else
sb.append(' ');
sb.append(lineS);
repeat(sb, ' ', padding);
sb.append(s);
}
// see if this module, line combo is the current location
boolean isCurrentLocation(int module, int line)
{
boolean yes = false;
Location l = getCurrentLocation();
if (l != null)
{
SourceFile file = l.getFile();
if (file != null && file.getId() == module && l.getLine() == line)
yes = true;
}
return yes;
}
private int parseLineNumber(String lineNumber) throws ParseException
{
try
{
return Integer.parseInt(lineNumber);
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", lineNumber); //$NON-NLS-1$
throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedLineNumber", args), 0); //$NON-NLS-1$
}
}
private int parseFileNumber(String fileNumber) throws ParseException
{
try
{
return Integer.parseInt(fileNumber);
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", fileNumber); //$NON-NLS-1$
throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedFileNumber", args), 0); //$NON-NLS-1$
}
}
private int parseFileName(String partialFileName) throws NoMatchException, AmbiguousException
{
SourceFile[] sourceFiles = m_fileInfo.getFiles(partialFileName);
int nSourceFiles = sourceFiles.length;
if (nSourceFiles == 0)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("name", partialFileName); //$NON-NLS-1$
throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noSourceFileWithSpecifiedName", args)); //$NON-NLS-1$
}
else if (nSourceFiles > 1)
{
String s = getLocalizationManager().getLocalizedTextString("ambiguousMatchingFilenames") + m_newline; //$NON-NLS-1$
for (int i = 0; i < nSourceFiles; i++)
{
SourceFile sourceFile = sourceFiles[i];
s += " " + sourceFile.getName() + "#" + sourceFile.getId(); //$NON-NLS-1$ //$NON-NLS-2$
if (i < nSourceFiles - 1)
s += m_newline;
}
throw new AmbiguousException(s);
}
return sourceFiles[0].getId();
}
/* used by parseFunctionName */
private static class ModuleFunctionPair implements Comparable<ModuleFunctionPair>
{
public ModuleFunctionPair(int moduleId, String functionName) {
this.moduleId = moduleId;
this.functionName = functionName;
}
public int moduleId;
public String functionName;
public int compareTo(ModuleFunctionPair other) {
return functionName.compareTo(other.functionName);
}
}
/**
* Parse a partial function name
* @param module the FIRST module to search; but we also search all the others if 'onlyThisModule' is false
* @return two ints: first is the module, and second is the line
*/
private int[] parseFunctionName(int module, String partialFunctionName, boolean onlyThisModule) throws NoMatchException, AmbiguousException
{
try { waitForMetaData(); } catch(InProgressException ipe) {} // wait a bit before we try this to give the background thread time to complete
SourceFile m = m_fileInfo.getFile(module, m_activeIsolate);
ArrayList<ModuleFunctionPair> functionNames = new ArrayList<ModuleFunctionPair>(); // each member is a ModuleFunctionPair
appendFunctionNamesMatching(functionNames, m, partialFunctionName);
if (functionNames.size() == 0)
{
if (!onlyThisModule)
{
// not found in the specified module; search all the other modules
for (Isolate isolate : m_session.getWorkers()) {
Iterator fileIter = m_fileInfo.getAllFiles(isolate.getId());
while (fileIter.hasNext())
{
SourceFile nextFile = (SourceFile) ((Map.Entry)fileIter.next()).getValue();
if (nextFile != m) // skip the one file we searched at the beginning
{
appendFunctionNamesMatching(functionNames, nextFile, partialFunctionName);
}
}
}
}
if (functionNames.size() == 0)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("name", partialFunctionName); //$NON-NLS-1$
throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noFunctionWithSpecifiedName", args)); //$NON-NLS-1$
}
}
if (functionNames.size() > 1)
{
ModuleFunctionPair[] functionNameArray = functionNames.toArray( new ModuleFunctionPair[functionNames.size()] );
Arrays.sort(functionNameArray);
String s = getLocalizationManager().getLocalizedTextString("ambiguousMatchingFunctionNames") + m_newline; //$NON-NLS-1$
Map<String, Object> args = new HashMap<String, Object>();
for (int i = 0; i < functionNameArray.length; i++)
{
//String moduleName = m_fileInfo.getFile(functionNameArray[i].moduleId).getName();
String moduleName = m_fileInfo.getFile(functionNameArray[i].moduleId, m_activeIsolate).getName();
String functionName = functionNameArray[i].functionName;
args.put("functionName", functionName); //$NON-NLS-1$
args.put("filename", moduleName + "#" + functionNameArray[i].moduleId); //$NON-NLS-1$ //$NON-NLS-2$
s += " " + getLocalizationManager().getLocalizedTextString("functionInFile", args); //$NON-NLS-1$ //$NON-NLS-2$
if (i < functionNameArray.length - 1)
s += m_newline;
}
throw new AmbiguousException(s);
}
ModuleFunctionPair pair = functionNames.get(0);
module = pair.moduleId;
m = m_fileInfo.getFile(module, m_activeIsolate);
int line = m.getLineForFunctionName(m_session, pair.functionName);
return new int[] { module, line };
}
/**
* Find function names in this module that start with
* the specified string, and append them to the specified List.
*
* If partialName contains parenthesis then we look for an exact match
*/
private void appendFunctionNamesMatching(List<ModuleFunctionPair> functionNameList, SourceFile m, String partialName)
{
int exactHitAt = -1;
// trim off the trailing parenthesis, if any
int parenAt = partialName.lastIndexOf('(');
if (parenAt > -1)
partialName = partialName.substring(0, parenAt);
String[] names = m.getFunctionNames(m_session);
for(int i=0; i<names.length; i++)
{
String functionName = names[i];
if (functionName.equals(partialName))
{
exactHitAt = i;
break;
}
else if (functionName.startsWith(partialName))
functionNameList.add(new ModuleFunctionPair(m.getId(), functionName));
}
// exact match?
if (exactHitAt > -1)
{
functionNameList.clear();
functionNameList.add(new ModuleFunctionPair(m.getId(), names[exactHitAt]));
}
}
/**
* Parse arg to determine which file it specifies.
* Allowed formats: #29, MyApp.mxml, MyA
* A variety of exceptions are thrown for other formats.
*/
public int parseFileArg(int module, String arg) throws ParseException, AmbiguousException, NoMatchException
{
/* Special case: a location arg like :15 produces a file arg
which is an empty string. */
if (arg.length() == 0)
return module;
char firstChar = arg.charAt(0);
/* The first character can't be 0-9 or '-'. */
if (Character.isDigit(firstChar) || firstChar == '-')
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", arg); //$NON-NLS-1$
throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedFile", args), 0); //$NON-NLS-1$
}
/* If the first character is '#', the rest must be a file number. */
else if (firstChar == '#')
{
return parseFileNumber(arg.substring(1));
}
/* Otherwise, assume beforeColon is a full or partial file name. */
else
{
return parseFileName(arg);
}
}
/**
* Parse arg to determine which line it specifies.
* Allowed formats: 17, MyFunction, MyF
* A variety of exceptions are thrown for other formats.
*/
public int parseLineArg(int module, String arg) throws ParseException, AmbiguousException, NoMatchException
{
/* Special case: a location arg like #29: produces a line arg
which is an empty string. */
if (arg.length() == 0)
return 1;
char firstChar = arg.charAt(0);
/* If the first character is 0-9 or '-', arg is assumed to be a line number. */
if (Character.isDigit(firstChar) || firstChar == '-')
{
return parseLineNumber(arg);
}
/* The first character can't be '#'. */
else if (firstChar == '#')
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", arg); //$NON-NLS-1$
throw new ParseException(getLocalizationManager().getLocalizedTextString("expectedLineNumber", args), 0); //$NON-NLS-1$
}
/* Otherwise, assume arg is a full or partial function name. */
else
{
int[] moduleAndLine = parseFunctionName(module, arg, true);
return moduleAndLine[1];
}
}
/**
* Parse arg to figure out what module and line it specifies.
*
* Allowed formats (assuming Button.as is file #29
* and the first line of MyFunction is line 17):
*
* arg module line
* 17 no change 17
* MyFunction no change 17
* MyF no change 17
* #29 29 1
* Button.as 29 1
* Bu 29 1
* #29:17 29 17
* #29:MyFunction 29 17
* #29:MyF 29 17
* Button.as:17 29 17
* Button.as:MyFunction 29 17
* Button.as:MyF 29 17
* Bu:17 29 17
* Bu:MyFunction 29 17
* Bu:MyF 29 17
*
* A variety of exceptions are thrown for other formats.
*/
public int[] parseLocationArg(int module, int line, String arg, int isolateId) throws ParseException, AmbiguousException, NoMatchException
{
int colonAt = arg.indexOf(':');
int wasFunc = 0; // set to 1 if a function was named
/* First deal with the case where arg doesn't contain a ':'
and therefore might be specifying either a file or a line. */
if (colonAt < 0)
{
char firstChar = arg.charAt(0);
/* If the first character is 0-9 or '-', arg is assumed to be a line number. */
if (Character.isDigit(firstChar) || firstChar == '-')
{
line = parseLineNumber(arg);
}
/* If the first character is a '#', what follows
is assumed to be a file number. */
else if (firstChar == '#')
{
module = parseFileNumber(arg.substring(1));
line = 1;
}
/* Otherwise, assume arg is a full or partial function name or file name. */
else
{
/* Assume arg is a full or partial function in the specified module. */
try
{
int[] moduleAndLine = parseFunctionName(module, arg, false);
module = moduleAndLine[0];
line = moduleAndLine[1];
wasFunc = 1;
}
/* If it isn't, assume arg is a full or partial file name. */
catch(NoMatchException pe)
{
try
{
module = parseFileName(arg);
line = 1;
}
catch(NoMatchException pee)
{
// catch the 'file name' string
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", arg); //$NON-NLS-1$
throw new NoMatchException(getLocalizationManager().getLocalizedTextString("noSuchFileOrFunction", args)); //$NON-NLS-1$
}
}
}
}
/* Now deal with the case where arg contains a ':',
and is therefore specifying both a file and a line. */
else
{
module = parseFileArg(module, arg.substring(0, colonAt));
line = parseLineArg(module, arg.substring(colonAt + 1));
wasFunc = (arg.substring(colonAt+1).length() > 1 && Character.isDigit(arg.substring(colonAt+1).charAt(0)) ) ? 0 : 1;
}
return new int[] { module, line, wasFunc };
}
/**
* Print the context of a Variable
*/
void doWhat() throws NotConnectedException
{
/* wait a bit if we are not halted */
waitTilHalted(m_activeIsolate);
try
{
Object result = null;
/* pull the rest of the line */
String s = restOfLine();
// first parse it, then attempt to evaluate the expression
ValueExp expr = parseExpression(s);
// make sure no assignment
if ( expr.containsAssignment() )
throw new IllegalAccessException();
result = evalExpression(expr, m_activeIsolate).value;
/* dump the output */
StringBuilder sb = new StringBuilder();
if (result instanceof Variable)
{
Variable v = (Variable) result;
// if it has a path then display it!
if (v.isAttributeSet(VariableAttribute.IS_LOCAL))
s = getLocalizationManager().getLocalizedTextString("localVariable"); //$NON-NLS-1$
else if (v.isAttributeSet(VariableAttribute.IS_ARGUMENT))
s = getLocalizationManager().getLocalizedTextString("functionArgumentVariable"); //$NON-NLS-1$
else if ( (v instanceof VariableFacade) && (s = ((VariableFacade)v).getPath()) != null && s.length() > 0 )
;
else
s = "_global"; //$NON-NLS-1$
sb.append(s);
}
else
sb.append(getLocalizationManager().getLocalizedTextString("mustBeOnlyOneVariable")); //$NON-NLS-1$
out( sb.toString() );
}
catch(IllegalAccessException iae)
{
err(getLocalizationManager().getLocalizedTextString("noSideEffectsAllowed")); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
}
}
/*
* We accept zero, one or two args for this command. Zero args
* means list the next 10 line around the previous listing. One argument
* specifies a line and 10 lines are listed around that line. Two arguments
* with a command between specifies starting and ending lines.
*/
void doList()
{
/* currentXXX may NOT be invalid! */
int currentModule = propertyGet(LIST_MODULE);
int currentLine = propertyGet(LIST_LINE);
int listsize = propertyGet(LIST_SIZE);
int workerid = propertyGet(LIST_WORKER);
String arg1 = null;
int module1 = currentModule;
int line1 = currentLine;
String arg2 = null;
int line2 = currentLine;
int numLines = 0;
try
{
if (hasMoreTokens())
{
arg1 = nextToken();
if (arg1.equals("-")) //$NON-NLS-1$
{
// move back two times the listing size and if listsize is odd then move forward one
line1 = line2 = line1 - (2 * listsize);
}
else
{
int[] result = parseLocationArg(currentModule, currentLine, arg1, workerid);
module1 = result[0];
line2 = line1 = result[1];
if (hasMoreTokens())
{
arg2 = nextToken();
line2 = parseLineArg(module1, arg2);
}
}
}
// System.out.println("1="+module1+":"+line1+",2=:"+line2);
/**
* Check for a few error conditions, otherwise we'll write a listing!
*/
if (hasMoreTokens())
{
err(getLocalizationManager().getLocalizedTextString("lineJunk")); //$NON-NLS-1$
}
else
{
int half = listsize/2;
SourceFile file = m_fileInfo.getFile(module1, workerid);
// SourceFile file = m_fileInfo.getFakeFile(module1);
numLines = file.getLineCount();
int newLine;
if (numLines == 1 && file.getLine(1).equals("")) //$NON-NLS-1$
{
// there's no source in the file at all!
// this presumably means that the source file isn't in the current directory
err(getLocalizationManager().getLocalizedTextString("sourceFileNotFound")); //$NON-NLS-1$
newLine = currentLine;
}
else
{
// pressing return is ok, otherwise throw the exception
if (line1 > numLines && arg1 != null)
throw new IndexOutOfBoundsException();
/* if no arg2 then user requested the next N lines around something */
if (arg2 == null)
{
line2 = line1 + (half) - 1;
line1 = line1 - (listsize-half);
}
/* adjust our range of lines to ensure we conform */
if (line1 < 1)
{
/* shrink line 1, grow line2 */
line2 += -(line1 - 1);
line1 = 1;
}
if (line2 > numLines)
line2 = numLines;
// System.out.println("1="+module1+":"+line1+",2="+module2+":"+line2+",num="+numLines+",half="+half);
/* nothing to display */
if (line1 > line2)
throw new IndexOutOfBoundsException();
/* now do it! */
SourceFile source = m_fileInfo.getFile(module1, workerid);
for(int i=line1; i<=line2; i++)
outputSource(module1, i, source.getLine(i));
newLine = line2 + half + (((listsize % 2) == 0) ? 1 : 2); // add one if even, 2 for odd;
}
/* save away valid context */
propertyPut(LIST_MODULE, module1);
propertyPut(LIST_LINE, newLine);
propertyPut(LIST_WORKER, workerid);
m_repeatLine = "list"; /* allow repeated listing by typing CR */ //$NON-NLS-1$
}
}
catch(IndexOutOfBoundsException iob)
{
String name = "#"+module1; //$NON-NLS-1$
Map<String, Object> args = new HashMap<String, Object>();
args.put("line", Integer.toString(line1)); //$NON-NLS-1$
args.put("filename", name); //$NON-NLS-1$
args.put("total", Integer.toString(numLines)); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("lineNumberOutOfRange", args)); //$NON-NLS-1$
}
catch(AmbiguousException ae)
{
err(ae.getMessage());
}
catch(NoMatchException nme)
{
// TODO [mmorearty]: try to find a matching source file
err(nme.getMessage());
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noFilesFound")); //$NON-NLS-1$
}
catch(ParseException pe)
{
err(pe.getMessage());
}
}
/**
* Fire up a session or await a connection from the socket if no
* URI was specified.
*/
void doRun() throws IOException
{
if (m_session != null)
{
err(getLocalizationManager().getLocalizedTextString("sessionInProgress")); //$NON-NLS-1$
return;
}
SessionManager mgr = Bootstrap.sessionManager();
if (hasMoreTokens())
{
if (!setLaunchURI(restOfLine()))
return;
}
if (m_connectPort == null)
mgr.startListening();
try
{
if (m_connectPort != null) {
out(getLocalizationManager().getLocalizedTextString("waitingToConnectToPlayer")); //$NON-NLS-1$
m_session = mgr.connect(Integer.valueOf(m_connectPort), null);
}
else if (m_launchURI == null)
{
out(getLocalizationManager().getLocalizedTextString("waitingForPlayerToConnect")); //$NON-NLS-1$
m_session = mgr.accept(null);
}
else
{
out(getLocalizationManager().getLocalizedTextString("launchingWithUrl") + m_newline + m_launchURI); //$NON-NLS-1$
m_session = mgr.launch(m_launchURI, null, true, null, null);
}
// now see what happened
if (m_session == null)
{
// shouldn't have gotten here
throw new SocketTimeoutException();
}
else
{
out(getLocalizationManager().getLocalizedTextString("playerConnectedSessionStarting")); //$NON-NLS-1$
initSession(m_session);
// pause for a while during startup, don't let exceptions ripple outwards
try { waitTilHalted(Isolate.DEFAULT_ID); } catch(Exception e) {}
// pause for a while during startup, don't let exceptions ripple outwards
try { waitForMetaData(); } catch(Exception e) {}
setInitialSourceFile();
out(getLocalizationManager().getLocalizedTextString("setBreakpointsThenResume")); //$NON-NLS-1$
setPromptState(InitialPromptState.DONE, Isolate.DEFAULT_ID);
// now poke to see if the player is good enough
try
{
if (m_session.getPreference(SessionManager.PLAYER_SUPPORTS_GET) == 0 )
err(m_newline + getLocalizationManager().getLocalizedTextString("warningNotAllCommandsSupported")); //$NON-NLS-1$
}
catch(Exception npe) {}
}
}
catch (FileNotFoundException fnf)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("uri", fnf.getLocalizedMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("fileDoesNotExist", args)); //$NON-NLS-1$
}
catch (SocketTimeoutException ste)
{
err(getLocalizationManager().getLocalizedTextString("failedToConnect")); //$NON-NLS-1$
}
catch (IOException io)
{
err(io.getLocalizedMessage());
}
finally
{
// turn off listening, to allow other session to connect
if (m_connectPort == null)
mgr.stopListening();
}
}
/**
* Fire up a session or await a connection from the socket if no
* URI was specified.
*/
void doConnect() throws IOException
{
int port = 0;
if (hasMoreTokens())
{
try
{
port = nextIntToken();
}
catch (NumberFormatException ex)
{
err(ex.getLocalizedMessage());
}
}
else
{
port = DProtocol.DEBUG_CONNECT_PORT;
}
if (port > 0)
{
m_connectPort = String.valueOf(port);
doRun();
}
}
/**
* When we begin a debugging session, it would be nice if the default file
* for the "list" command etc. was the user's main MXML application file.
* There is no good way to really figure out what that file is, but we can
* certainly take a guess.
*/
private void setInitialSourceFile()
{
int largestAuthoredId = -1;
SourceFile[] files = m_fileInfo.getFileList(m_activeIsolate);
for (int i=0; i<files.length; ++i)
{
SourceFile sf = files[i];
if (sf.getId() > largestAuthoredId && getFileType(sf) == AUTHORED_FILE)
largestAuthoredId = sf.getId();
}
if (largestAuthoredId != -1)
setListingPosition(largestAuthoredId, 1, m_activeIsolate);
}
private boolean setLaunchURI(String launchURI)
{
if (launchURI != null)
{
SessionManager mgr = Bootstrap.sessionManager();
// If doing fdbunit, we always try to do launch(), even on platforms
// that say they don't support it
if (!mgr.supportsLaunch() && System.getProperty("fdbunit") == null) //$NON-NLS-1$
{
err(getLocalizationManager().getLocalizedTextString("manuallyLaunchPlayer")); //$NON-NLS-1$
return false;
}
// check for special form of URI when in fullname mode, since we can't pass http: in this mode?!?
if (m_fullnameOption)
{
if (launchURI.startsWith("//")) //$NON-NLS-1$
launchURI = "http:"+launchURI; //$NON-NLS-1$
}
}
m_launchURI = launchURI;
return true;
}
// set the URI
void doFile()
{
if (!hasMoreTokens())
setLaunchURI(null);
else
setLaunchURI(restOfLine());
}
void doSource()
{
String name = ""; //$NON-NLS-1$
try
{
name = nextToken();
FileReader f = new FileReader(name);
// push our current source onto the stack and open the new one
pushStream(m_in);
m_in = new LineNumberReader(f);
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("sourceCommandRequiresPath")); //$NON-NLS-1$
}
catch(NoSuchElementException nse)
{
err(getLocalizationManager().getLocalizedTextString("sourceCommandRequiresPath")); //$NON-NLS-1$
}
catch(FileNotFoundException fnf)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("filename", name); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("fileNotFound", args)); //$NON-NLS-1$
}
}
void listFault(String f)
{
StringBuilder sb = new StringBuilder();
appendFaultTitles(sb);
appendFault(sb, f);
out( sb.toString() );
}
void appendFault(StringBuilder sb, String f)
{
sb.append(f);
int space = 30 - f.length();
repeat(sb, ' ', space);
boolean stop = m_faultTable.is(f, "stop"); //$NON-NLS-1$
boolean print = m_faultTable.is(f, "print"); //$NON-NLS-1$
sb.append( stop ? "Yes" : "No" ); //$NON-NLS-1$ //$NON-NLS-2$
repeat(sb, ' ', stop ? 0 : 1);
repeat(sb, ' ', 5);
sb.append( print ? "Yes" : "No" ); //$NON-NLS-1$ //$NON-NLS-2$
repeat(sb, ' ', print ? 0 : 1);
// description
repeat(sb, ' ', 7);
String desc = m_faultTable.getDescription(f);
sb.append(desc);
sb.append(m_newline);
}
void appendFaultTitles(StringBuilder sb)
{
sb.append("Fault Stop Print Description"+m_newline); //$NON-NLS-1$
sb.append("----- ---- ----- -----------"+m_newline); //$NON-NLS-1$
}
/**
* Controls the configuration of what occurs when a
* fault is encountered
*/
void doHandle()
{
// should be at least on arg
if (!hasMoreTokens())
err(getLocalizationManager().getLocalizedTextString("argumentRequired")); //$NON-NLS-1$
else
{
// poor man's fix for supporting 'all' option
String faultName = nextToken();
Object[] names = new Object[] { faultName };
// replace the single name with all of them
if (faultName.equalsIgnoreCase("all")) //$NON-NLS-1$
names = m_faultTable.names();
// make sure we know about at least one
if (!m_faultTable.exists((String)names[0]))
err(getLocalizationManager().getLocalizedTextString("unrecognizedFault")); //$NON-NLS-1$
else
{
if (!hasMoreTokens())
listFault((String)names[0]);
else
{
String action = null;
try
{
while(hasMoreTokens())
{
action = nextToken();
for(int i=0; i<names.length; i++)
m_faultTable.action((String)names[i], action);
}
}
catch(IllegalArgumentException iae)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("action", action); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("unrecognizedAction", args)); //$NON-NLS-1$
}
}
}
}
}
/**
* Do the commands command! This attaches a series of lines of text input by the user
* to a particular breakpoint, with the intention of exeuting these lines when the
* breakpoint is hit.
*/
void doCommands() throws IOException
{
try
{
int id = -1;
if (hasMoreTokens())
id = nextIntToken();
else
id = propertyGet(BPNUM);
// get the breakpoint
int at = breakpointIndexOf(id);
BreakAction a = breakpointAt(at);
// ready it
a.clearCommands();
a.setSilent(false);
// now just pull the commands as they come while not end
String line = null;
boolean first = true;
boolean isEnd = false;
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(id)); //$NON-NLS-1$
out(getLocalizationManager().getLocalizedTextString("typeCommandsForBreakpoint", args)); //$NON-NLS-1$
do
{
displayCommandPrompt();
line = readLine().trim();
isEnd = line.equalsIgnoreCase("end"); //$NON-NLS-1$
if (!isEnd)
{
if (first && line.equalsIgnoreCase("silent")) //$NON-NLS-1$
a.setSilent(true);
else
a.addCommand(line);
}
first = false;
}
while(!isEnd);
}
catch(IndexOutOfBoundsException iob)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
}
}
/**
* Apply or remove conditions to a breakpoint.
*/
void doCondition() throws IOException
{
try
{
// must have a breakpoint number
int id = nextIntToken();
// get the breakpoint
int at = breakpointIndexOf(id);
BreakAction a = breakpointAt(at);
// no more parms means to clear it
if (hasMoreTokens())
{
// now just pull the commands as they come while not end
String line = restOfLine();
// build an expression and attach it to the breakpoint
ValueExp exp = parseExpression(line);
// warn about the assignment!
if ( exp.containsAssignment() && !yesNoQuery(getLocalizationManager().getLocalizedTextString("askExpressionContainsAssignment")) ) //$NON-NLS-1$
throw new IllegalAccessException("="); //$NON-NLS-1$
a.setCondition(exp, line);
}
else
{
a.clearCondition(); // clear it
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(id)); //$NON-NLS-1$
out(getLocalizationManager().getLocalizedTextString("breakpointNowUnconditional", args)); //$NON-NLS-1$
}
}
catch(IllegalAccessException iae)
{
err(getLocalizationManager().getLocalizedTextString("breakpointNotChanged")); //$NON-NLS-1$
}
catch(IndexOutOfBoundsException iob)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
}
}
/**
* Request to add a new watchpoint
* This may result in one of two things happening
* (1) a new watchpoint could be added or
* (2) an existing watchpoint may be modified.
*
* The watch, awatch, and rwatch commands will set a watchpoint on the
* given expression. The different commands control the read/write aspect
* of the watchpoint.
*
* awatch will trigger a break if the expression is read or written.
* rwatch will trigger a break if the expression is read.
* watch will trigger a break if the expression is written.
*/
void doWatch(boolean read, boolean write) throws PlayerDebugException
{
try
{
if (read)
{
err("Only break-on-write watchpoints are supported."); //$NON-NLS-1$
return;
}
StringBuilder sb = new StringBuilder();
/* pull the rest of the line */
String s = restOfLine();
int flags = 3;
if(read && write) flags = WatchKind.READWRITE;
else if(read) flags = WatchKind.READ;
else if(write) flags = WatchKind.WRITE;
IsolateSession workerSession = m_session.getWorkerSession(m_activeIsolate);
// snapshot of our existing list
Watch[] list = workerSession.getWatchList();
// We need to separate the front part the 'a.b' in 'a.b.c'
// of the expression to resolve it into a variable
// We usually get back a VariableFacade which contains
// the context id (i.e the variable id) and the member name.
ValueExp expr = parseExpression(s);
VariableFacade result = (VariableFacade)(evalExpression(expr, m_activeIsolate).value);
// extract the 2 pieces and get the raw variable.
long varId = result.getContext(); // TODO fix this??? -mike
String memberName = result.getName();
Value v = workerSession.getValue(varId);
// attempt to set.
Watch w = m_session.setWatch(v, memberName, flags);
if (w == null)
{
// failed
Map<String, Object> args = new HashMap<String, Object>();
args.put("expression", s); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("watchpointCouldNotBeSet", args)); //$NON-NLS-1$
}
else
{
// if modified then lists are same length
// otherwise 1 will be added
Watch[] newList = workerSession.getWatchList();
if (newList.length == list.length)
{
// modified, lets locate the one that changed
// and reset it
int at = missingWatchpointIndexOf(newList);
WatchAction a = null;
try
{
a = watchpointAt(at);
}
catch(ArrayIndexOutOfBoundsException aio)
{
// this is pretty bad it means the player thinks we have a watchpoint
// but we don't have a record of it. So let's create a new one
// and hope that we are now in sync with the player.
a = new WatchAction(w);
}
// modify our view of the watchpoint
int id = a.getId();
a.resetWatch(w);
Map<String, Object> args = new HashMap<String, Object>();
args.put("watchpointNumber", Integer.toString(id)); //$NON-NLS-1$
args.put("expression", s); //$NON-NLS-1$
args.put("watchpointMode", getWatchpointModeString(a.getKind())); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("changedWatchpointMode", args)); //$NON-NLS-1$
}
else
{
// newly added
WatchAction a = new WatchAction(w);
watchpointAdd(a);
int which = a.getId();
Map<String, Object> args = new HashMap<String, Object>();
args.put("watchpointNumber", Integer.toString(which)); //$NON-NLS-1$
args.put("expression", s); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("createdWatchpoint", args)); //$NON-NLS-1$
}
out(sb.toString());
}
}
catch(ArrayIndexOutOfBoundsException aio)
{
// We should really do some cleanup after this exception
// since it most likely means we can't find the watchpoint
// that was just modified, therefore our watchlists are
// out of sync with those of the API.
err(getLocalizationManager().getLocalizedTextString("badWatchpointNumber")); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
}
catch(ClassCastException cce)
{
err(getLocalizationManager().getLocalizedTextString("couldNotResolveExpression")); //$NON-NLS-1$
}
}
WatchAction watchpointAt(int at) { return m_watchpoints.get(at); }
boolean watchpointAdd(WatchAction a) { return m_watchpoints.add(a); }
int watchpointCount() { return m_watchpoints.size(); }
int watchpointIndexOf(int id)
{
int size = watchpointCount();
for(int i = 0; i < size; i++)
{
WatchAction b = watchpointAt(i);
if(b.getId() == id)
return i;
}
return -1;
}
void removeAllWatchpoints() throws NotConnectedException
{
while(watchpointCount() > 0)
removeWatchpointAt(0);
}
void removeWatchpointAt(int at) throws NotConnectedException
{
WatchAction b = watchpointAt(at);
boolean worked = false;
try { worked = (m_session.clearWatch(b.getWatch()) == null) ? false : true; } catch(NoResponseException nre) {}
if(!worked)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("variable", b.getExpr() ); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("couldNotFindWatchpoint", args)); //$NON-NLS-1$
}
// remove in any event
m_watchpoints.remove(at);
}
String getWatchpointModeString(int flags)
{
switch(flags)
{
case 1:
return getLocalizationManager().getLocalizedTextString("watchpointMode_read"); //$NON-NLS-1$
case 2:
return getLocalizationManager().getLocalizedTextString("watchpointMode_write"); //$NON-NLS-1$
case 3:
return getLocalizationManager().getLocalizedTextString("watchpointMode_readWrite"); //$NON-NLS-1$
}
return ""; //$NON-NLS-1$
}
/**
* Locate the index of a WatchAction that does not
* have a corresponding Watch in the given list.
*
* WARNING: this call can be very expensive but
* it is assumed that a.list and watchpointCount()
* are both small.
*/
int missingWatchpointIndexOf(Watch[] a)
{
int size = watchpointCount();
int at = -1;
for(int i=0; i<size && at<0; i++)
{
WatchAction action = watchpointAt(i);
Watch w = action.getWatch();
// now scan the list of watches looking for a hit
int hit = -1;
for(int j=0; j<a.length && hit<0; j++)
{
if (w == a[j])
hit = j;
}
// can't find the watch object corresponding to our
// watchpoint in list of session watches.
if (hit < 0)
at = i;
}
return at;
}
/**
* Display command
*/
void doDisplay()
{
try
{
if (!hasMoreTokens())
doInfoDisplay();
else
{
// followed by an expression (i.e. a line we just pull in)
String s = restOfLine();
// first parse it, then attempt to evaluate the expression
ValueExp expr = parseExpression(s);
// make sure no assignment
if ( expr.containsAssignment() )
throw new IllegalAccessException();
// it worked so create a new DisplayAction and then add it in
DisplayAction b = new DisplayAction(expr, s, m_activeIsolate);
b.setEnabled(true);
displayAdd(b);
}
}
catch(IllegalAccessException iae)
{
err(getLocalizationManager().getLocalizedTextString("noSideEffectsAllowed")); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
// already handled by parseExpression
}
}
/**
* Remove auto-display expressions
*/
void doUnDisplay() throws IOException
{
try
{
if (!hasMoreTokens())
{
// no args means delete all displays, last chance...
if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askDeleteAllAutoDisplay"))) //$NON-NLS-1$
{
int count = displayCount();
for(int i=count-1; i>-1; i--)
{
displayRemoveAt(i);
}
}
}
else
{
while(hasMoreTokens())
{
int id = nextIntToken();
int at = displayIndexOf(id);
displayRemoveAt(at);
}
}
}
catch(IndexOutOfBoundsException iob)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("displayNumber", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("noDisplayNumber", args)); //$NON-NLS-1$
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("badDisplayNumber", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
}
}
/**
* Enabled breakpoints and disaplays
*/
void doDisable() throws AmbiguousException, NotConnectedException
{
waitTilHalted(m_activeIsolate);
try
{
if (!hasMoreTokens())
{
disableAllBreakpoints();
}
else
{
// optionally specify 'display' or 'breakpoint'
String arg = nextToken();
int cmd = disableCommandFor(arg);
int id = -1;
if (cmd == CMD_DISPLAY)
{
doDisableDisplay();
}
else if (cmd == CMD_BREAK && !hasMoreTokens())
{
disableAllBreakpoints();
}
else
{
if (cmd == CMD_BREAK)
id = nextIntToken(); // ignore and get next number token
else
id = Integer.parseInt(arg);
do
{
int at = breakpointIndexOf(id);
disableBreakpointAt(at);
if (hasMoreTokens())
id = nextIntToken();
else
id = -1;
// keep going till we're blue in the face; also note that we cache'd a copy of locations
// so that breakpoint numbers are consistent.
}
while( id > -1 );
}
}
}
catch(IndexOutOfBoundsException iob)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
}
}
void disableAllBreakpoints() throws NotConnectedException {
// disables all breakpoints,
int count = breakpointCount();
for(int i=0; i<count; i++)
disableBreakpointAt(i);
}
// disable a breakpoint
void disableBreakpointAt(int at) throws NotConnectedException
{
BreakAction a = breakpointAt(at);
a.setEnabled(false);
breakDisableRequest(a.getLocations());
}
void doDisableDisplay() { doEnableDisableDisplay(false); }
void doEnableDisableDisplay(boolean enable)
{
try
{
if (!hasMoreTokens())
{
// means do all!
int size = displayCount();
for(int i=0; i<size; i++)
displayAt(i).setEnabled(enable);
}
else
{
// read ids until no more
while( (hasMoreTokens()) )
{
int id = nextIntToken();
int at = displayIndexOf(id);
DisplayAction a = displayAt(at);
a.setEnabled(enable);
}
}
}
catch(IndexOutOfBoundsException iob)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("displayNumber", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("noDisplayNumber", args)); //$NON-NLS-1$
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("badDisplayNumber", args)); //$NON-NLS-1$
}
}
/**
* Enables breakpoints (forever, one-shot hit or auto delete) and displays
*/
void doEnable() throws AmbiguousException, NotConnectedException
{
waitTilHalted(m_activeIsolate);
try
{
if (!hasMoreTokens())
{
enableAllBreakpoints();
}
else
{
// optionally specify 'display' or 'breakpoint'
String arg = nextToken();
int cmd = enableCommandFor(arg);
int id = -1;
boolean autoDelete = false;
boolean autoDisable = false;
if (cmd == CMD_DISPLAY)
{
doEnableDisplay();
}
else if ((cmd == CMD_BREAK) && !hasMoreTokens())
{
enableAllBreakpoints();
}
else
{
if (cmd == CMD_BREAK)
id = nextIntToken(); // ignore and get next number token
else if (cmd == CMD_DELETE)
{
autoDelete = true;
id = nextIntToken(); // set and get next number token
}
else if (cmd == ENABLE_ONCE_CMD)
{
autoDisable = true;
id = nextIntToken(); // set and get next number token
}
else
id = Integer.parseInt(arg);
boolean worked = true;
do
{
int at = breakpointIndexOf(id);
worked = enableBreakpointAt(at, autoDisable, autoDelete);
if (hasMoreTokens())
id = nextIntToken();
else
id = -1;
// keep going till we're blue in the face; also note that we cache'd a copy of locations
// so that breakpoint numbers are consistent.
}
while( worked && id > -1 );
if (!worked)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", Integer.toString(id)); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("breakpointLocationNoLongerExists", args)); //$NON-NLS-1$
}
}
}
}
catch(IndexOutOfBoundsException iob)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("breakpointNumber", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("noBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("token", m_currentToken); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("badBreakpointNumber", args)); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("commandFailed")); //$NON-NLS-1$
}
}
void enableAllBreakpoints() throws NotConnectedException {
// enables all breakpoints
int count = breakpointCount();
int tally = 0;
for(int i=0; i<count; i++)
tally += enableBreakpointAt(i) ? 1 : 0;
// mention that not all was good
if (tally != count)
err(getLocalizationManager().getLocalizedTextString("notAllBreakpointsEnabled")); //$NON-NLS-1$
}
// request to enable a breakpoint
// @return false if we couldn't enable it.
boolean enableBreakpointAt(int at) throws NotConnectedException { return enableBreakpointAt(at, false, false); }
boolean enableBreakpointAt(int at, boolean autoDisable, boolean autoDelete) throws NotConnectedException
{
return enableBreakpoint(breakpointAt(at), autoDisable, autoDelete);
}
boolean enableBreakpoint(BreakAction a, boolean autoDisable, boolean autoDelete) throws NotConnectedException
{
boolean retval = false;
Location l = a.getLocation(); // use the first location as a source file / line number template
if (l != null)
{
LocationCollection col = enableBreak(l.getFile(), l.getLine(), l.getIsolateId());
if (!col.isEmpty())
{
a.setEnabled(true);
a.setLocations(col);
a.setAutoDisable(autoDisable);
a.setAutoDelete(autoDelete);
a.setSingleSwf(false);
a.setStatus(BreakAction.RESOLVED);
retval = true;
}
}
return retval;
}
void doEnableDisplay() { doEnableDisableDisplay(true); }
/* Print working directory */
void doPWD()
{
out(System.getProperty("user.dir")); //$NON-NLS-1$
}
/* Display or change current file */
void doCF()
{
try
{
int module = propertyGet(LIST_MODULE);
int currentLine = propertyGet(LIST_LINE);
int worker = propertyGet(LIST_WORKER);
if (hasMoreTokens())
{
String arg = nextToken();
module = parseFileArg(module, arg);
currentLine = 1;
setListingPosition(module, currentLine, worker);
}
SourceFile sourceFile = m_fileInfo.getFile(module, worker);
StringBuilder sb = new StringBuilder();
sb.append(sourceFile.getName());
sb.append('#');
sb.append(sourceFile.getId());
sb.append(':');
sb.append(currentLine);
out( sb.toString() );
}
catch(NullPointerException npe)
{
err(getLocalizationManager().getLocalizedTextString("noFilesFound")); //$NON-NLS-1$
}
catch(ParseException pe)
{
err(pe.getMessage());
}
catch(AmbiguousException ae)
{
err(ae.getMessage());
}
catch(NoMatchException nme)
{
err(nme.getMessage());
}
}
/* Terminates current debugging sesssion */
void doKill() throws IOException
{
if (m_session == null)
err(getLocalizationManager().getLocalizedTextString("programNotBeingRun")); //$NON-NLS-1$
else
{
if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askKillProgram"))) //$NON-NLS-1$
exitSession();
}
}
/* Terminates fdb */
boolean doQuit() throws IOException
{
boolean quit = false;
// no session, no questions
if (m_session == null)
quit = true;
else
{
quit = yesNoQuery(getLocalizationManager().getLocalizedTextString("askProgramIsRunningExitAnyway")); //$NON-NLS-1$
if (quit)
exitSession();
}
return quit;
}
/* (non-Javadoc)
* @see flash.tools.debugger.SourceLocator#locateSource(java.lang.String, java.lang.String, java.lang.String)
*/
public InputStream locateSource(String path, String pkg, String name)
{
File f = null;
boolean exists = false;
String pkgPlusName;
if ((pkg != null && pkg.length() > 0))
pkgPlusName = pkg + File.separator + name;
else
pkgPlusName = null;
Iterator<String> iter = m_sourceDirectories.iterator();
while (iter.hasNext())
{
String dir = iter.next();
// new File("", filename) searches the root dir -- that's not what we want!
if (dir.equals("")) //$NON-NLS-1$
dir = "."; //$NON-NLS-1$
// look for sourcedir\package\filename
if (pkgPlusName != null)
{
f = new File(dir, pkgPlusName);
exists = f.exists();
}
// look for sourcedir\filename
if (!exists)
{
f = new File(dir, name);
exists = f.exists();
}
if (exists)
{
try
{
return new FileInputStream(f);
}
catch (FileNotFoundException e)
{
e.printStackTrace(); // shouldn't happen
}
}
}
return null;
}
/* (non-Javadoc)
* @see flash.tools.debugger.SourceLocator#getChangeCount()
*/
public int getChangeCount()
{
return m_sourceDirectoriesChangeCount;
}
private void doDirectory() throws IOException
{
if (hasMoreTokens())
{
// File.separator is ";" on Windows or ":" on Mac
StringTokenizer dirs = new StringTokenizer(restOfLine(), File.pathSeparator);
int insertPos = 0;
while (dirs.hasMoreTokens())
{
String dir = dirs.nextToken();
if (dir.length() > 2 && dir.charAt(0) == '"' && dir.charAt(dir.length()-1) == '"')
dir = dir.substring(1, dir.length() - 1);
dir = dir.trim();
if (dir.length() > 0)
{
// For Unix and Mac, we want to escape "~" and "$HOME"
if (!System.getProperty("os.name").toLowerCase().startsWith("windows")) //$NON-NLS-1$ //$NON-NLS-2$
{
// If the string starts with "~", or contains any environment variables
// such as "$HOME", we need to escape those
if (dir.matches("^.*[~$].*$")) //$NON-NLS-1$
{
try
{
Process p = Runtime.getRuntime().exec(
new String[] { "/bin/sh", "-c", "echo " + dir} ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = r.readLine();
if (line != null)
{
line = line.trim();
if (line.length() > 0)
dir = line;
}
}
catch (IOException e)
{
// ignore
}
}
}
try
{
dir = new File(dir).getCanonicalPath();
m_sourceDirectories.add(insertPos++, dir);
}
catch (IOException e)
{
err(e.getMessage());
}
}
}
++m_sourceDirectoriesChangeCount;
}
else
{
if (yesNoQuery(getLocalizationManager().getLocalizedTextString("askReinitSourcePath"))) //$NON-NLS-1$
{
initSourceDirectoriesList();
}
}
doShowDirectories();
}
public void initSourceDirectoriesList()
{
m_sourceDirectories.clear();
File flexHome = getFlexHomeDirectory();
if (flexHome != null)
{
try
{
File projectsDir = new File(flexHome, "frameworks/projects"); //$NON-NLS-1$
File[] files = projectsDir.listFiles();
if (files != null)
{
for (int i=0; i<files.length; ++i)
{
if (files[i].isDirectory())
{
File srcDir = new File(files[i], "src"); //$NON-NLS-1$
if (srcDir.isDirectory())
{
m_sourceDirectories.add(srcDir.getCanonicalPath());
}
}
}
}
}
catch (IOException e)
{
// ignore
}
}
++m_sourceDirectoriesChangeCount;
}
/**
* Returns the Flex home directory. This is based on the <code>application.home</code>
* Java system property if present, or the current directory otherwise.
* This directory is one up from the "bin" and "lib" directories. For example,
* <code>&lt;flexhome&gt;/lib/fdb.jar</code> can be used to refer to the fdb jar.
*/
protected File getFlexHomeDirectory()
{
if (!m_initializedFlexHomeDirectory)
{
m_initializedFlexHomeDirectory = true;
m_flexHomeDirectory = new File("."); // default in case the following logic fails //$NON-NLS-1$
String flexHome = System.getProperty("application.home"); //$NON-NLS-1$
if (flexHome != null && flexHome.length() > 0)
{
try
{
m_flexHomeDirectory = new File(flexHome).getCanonicalFile();
}
catch (IOException e)
{
// ignore
}
}
}
return m_flexHomeDirectory;
}
protected String getenv(String var)
{
String[] cmd;
if (System.getProperty("os.name").toLowerCase().startsWith("windows")) //$NON-NLS-1$ //$NON-NLS-2$
{
cmd = new String[] { "cmd.exe", "/c", "echo", "%" + var + "%" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
else
{
cmd = new String[] { "/bin/sh", "-c", "echo $" + var }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
try
{
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = r.readLine();
if (line != null && line.length() > 0)
{
return line;
}
}
catch (IOException e)
{
// ignore
}
return null;
}
private void doCatch() throws NotConnectedException, NotSuspendedException, NoResponseException
{
/* wait a bit if we are not halted */
waitTilHalted(m_activeIsolate);
String typeToCatch = null;
/* currentXXX may NOT be invalid! */
if (!hasMoreTokens())
{
err("Catch requires an exception name."); //$NON-NLS-1$
return;
}
typeToCatch = nextToken();
if (typeToCatch == null || typeToCatch.length() == 0)
{
err("Illegal argument"); //$NON-NLS-1$
return;
}
Value type = null;
if (typeToCatch.equals("*")) //$NON-NLS-1$
{
typeToCatch = null;
}
else
{
type = getSession().getWorkerSession(m_activeIsolate).getGlobal(typeToCatch);
if (type == null)
{
err("Type not found."); //$NON-NLS-1$
return;
}
String typeName = type.getTypeName();
int at = typeName.indexOf('@');
if (at != -1)
typeName = typeName.substring(0, at);
if (!typeName.endsWith("$")) //$NON-NLS-1$
{
err("Not a type: " + type); //$NON-NLS-1$
return;
}
}
CatchAction c;
try {
c = addCatch(typeToCatch);
} catch (NotSupportedException e) {
e.printStackTrace();
return;
}
Map<String, Object> args = new HashMap<String, Object>();
args.put("id", c.getId()); //$NON-NLS-1$
c.getId();
}
private CatchAction addCatch(String typeToCatch) throws NotSupportedException, NoResponseException {
CatchAction c = new CatchAction(typeToCatch);
catchpointAdd(c);
return c;
}
CatchAction catchpointAt(int at) { return m_catchpoints.get(at); }
int catchpointCount() { return m_catchpoints.size(); }
boolean catchpointAdd(CatchAction a) throws NotSupportedException, NoResponseException
{
if (catchpointCount() == 0)
getSession().getWorkerSession(m_activeIsolate).breakOnCaughtExceptions(true);
return m_catchpoints.add(a);
}
int catchpointIndexOf(int id)
{
int size = catchpointCount();
for(int i = 0; i < size; i++)
{
CatchAction c = catchpointAt(i);
if(c.getId() == id)
return i;
}
return -1;
}
void removeAllCatchpoints() throws NotConnectedException
{
while(catchpointCount() > 0)
removeCatchpointAt(0);
}
void removeCatchpointAt(int at) throws NotConnectedException
{
// remove in any event
m_catchpoints.remove(at);
if (catchpointCount() == 0) {
try {
getSession().getWorkerSession(m_activeIsolate).breakOnCaughtExceptions(false);
} catch (NotSupportedException e) {
} catch (NoResponseException e) {
}
}
}
void doUnknown(String s) { doUnknown("", s); } //$NON-NLS-1$
void doUnknown(String what, String s)
{
Map<String, Object> args = new HashMap<String, Object>();
String formatString;
args.put("command", s); //$NON-NLS-1$
if (what == null || what.equals("")) //$NON-NLS-1$
{
formatString = "unknownCommand"; //$NON-NLS-1$
args.put("commandCategory", what); //$NON-NLS-1$
}
else
{
formatString = "unknownSubcommand"; //$NON-NLS-1$
}
err(getLocalizationManager().getLocalizedTextString(formatString, args));
}
/**
* Process the incoming debug event queue
*/
void processEvents() throws NotConnectedException
{
boolean requestResume = false;
int breakIsolate = Isolate.DEFAULT_ID;
boolean requestHalt = getIsolateState(breakIsolate).m_requestHalt;
while(m_session != null && m_session.getEventCount() > 0)
{
DebugEvent e = m_session.nextEvent();
if (e instanceof TraceEvent)
{
dumpTraceLine(e.information);
}
else if (e instanceof SwfLoadedEvent)
{
handleSwfLoadedEvent((SwfLoadedEvent)e);
}
else if (e instanceof SwfUnloadedEvent)
{
handleSwfUnloadedEvent((SwfUnloadedEvent)e);
}
else if (e instanceof IsolateCreateEvent) {
handleIsolateCreateEvent((IsolateCreateEvent)e);
}
else if (e instanceof IsolateExitEvent) {
handleIsolateExitEvent((IsolateExitEvent)e);
}
else if (e instanceof BreakEvent)
{
// store away isolate information
breakIsolate = ((BreakEvent) e).isolateId;
m_breakIsolates.add(breakIsolate);
/** If this break event is due to a freshly loaded swf,
* we need to mark it for showing prompt. */
int reason = SuspendReason.Unknown;
try { reason = m_session.getWorkerSession(breakIsolate).suspendReason(); } catch ( PlayerDebugException pde ) { }
if ( reason == SuspendReason.ScriptLoaded ) {
setPromptState(InitialPromptState.NEVER_SHOWN, breakIsolate);
}
break;
}
else if (e instanceof FileListModifiedEvent)
{
// we ignore this
}
else if (e instanceof FunctionMetaDataAvailableEvent)
{
// we ignore this
}
else if (e instanceof FaultEvent)
{
breakIsolate = ((FaultEvent) e).isolateId;
m_breakIsolates.add(breakIsolate);
if ( handleFault((FaultEvent)e) )
requestResume = true;
else
requestHalt = true;
break;
}
else
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("type", e); //$NON-NLS-1$
args.put("info", e.information); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("unknownEvent", args)); //$NON-NLS-1$
}
}
// only if we have processed a fault which requested a resume and no other fault asked for a break
// and we are suspended and it was due to us that the stop occurred!
IsolateSession workerSession = null;
if (m_session != null)
workerSession = m_session.getWorkerSession(breakIsolate);
if (requestResume && !requestHalt && workerSession != null &&
workerSession.isSuspended() && workerSession.suspendReason() == SuspendReason.Fault)
getIsolateState(breakIsolate).m_requestResume = true;
}
private void handleIsolateExitEvent(IsolateExitEvent e) {
dumpIsolateExitLine(e);
}
private void handleIsolateCreateEvent(IsolateCreateEvent e) {
dumpIsolateCreatedLine(e);
}
void dumpIsolateCreatedLine(IsolateCreateEvent e)
{
StringBuilder sb = new StringBuilder();
sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenWorkerCreated")); //$NON-NLS-1$
sb.append(' ');
sb.append(e.isolate.getId() - 1);
out(sb.toString());
}
void dumpIsolateExitLine(IsolateExitEvent e)
{
StringBuilder sb = new StringBuilder();
sb.append(getLocalizationManager().getLocalizedTextString("linePrefixWhenWorkerExit")); //$NON-NLS-1$
sb.append(' ');
sb.append(e.isolate.getId() - 1);
out(sb.toString());
}
/**
* Our logic for handling a break condition.
*
* @return some hit breakpoint requested silence, shhhh!
*/
boolean processBreak(boolean postStep, StringBuilder sb, int isolateId) throws NotConnectedException
{
Location l = getCurrentLocationIsolate(isolateId);
if (l == null || l.getFile() == null)
return false;
int fileId = l.getFile().getId();
int line = l.getLine();
boolean isSilent = false;
boolean bpHit = false;
boolean stoppedDueToBp = false;
int count = breakpointCount();
boolean[] markedForRemoval = new boolean[count];
DebugCLIIsolateState state = getIsolateState(isolateId);
boolean previousResume = state.m_requestResume;
IsolateSession workerSession = m_session.getWorkerSession(isolateId);
for(int i=0; i<count; i++)
{
BreakAction a = breakpointAt(i);
if (a.locationMatches(fileId, line))
{
/**
* Note that it appears that we stopped due to hitting a hard breakpoint
* Now if the breakpoint is conditional it may eval to false, meaning we
* won't stop here, otherwise we will process the breakpoint.
*/
stoppedDueToBp = (workerSession.suspendReason() == SuspendReason.Breakpoint);
if (shouldBreak(a, fileId, line, isolateId))
{
// its a hit
bpHit = true;
a.hit();
isSilent = (isSilent) ? true : a.isSilent();
// autodelete, autodisable
if (a.isAutoDisable())
disableBreakpointAt(i);
if (a.isAutoDelete())
markedForRemoval[i] = true;
// now issue any commands that are attached to the breakpoint
int n = a.getCommandCount();
for(int j=0 ;j<n; j++)
issueCommand(a.commandAt(j), sb);
}
}
}
// kill them backwards so our i is acurate
for(int i=markedForRemoval.length-1; i>-1; i--)
if (markedForRemoval[i])
removeBreakpointAt(i);
/**
* Now we should request to resume only if it was due to
* breakpoints that were hit.
*
* For the first case, we hit a conditional breakpoint that
* eval'd to false, resulting in bpHit == false. Thus we
* want to resume and additionally if we were stepping, we'd
* like to do so 'softly' that is without loosing the stepping
* information on the Player.
*
* For the 2nd case, we hit a breakpoint and we executed
* commands that resulted in a m_requestResume.
*/
if (stoppedDueToBp && !bpHit)
{
state.m_requestResume = true;
state.m_stepResume = postStep; // resume without losing our stepping
isSilent = true; // do so quietly
}
else if (stoppedDueToBp && bpHit && state.m_requestResume && !previousResume)
{
state.m_requestResume = true;
state.m_stepResume = postStep; // resume as we would
processDisplay(sb);
}
// If we aren't continuing, then show display variables
if (!state.m_requestResume)
processDisplay(sb);
// System.out.println("processBreak stopDueToBp="+stoppedDueToBp+",bpHit="+bpHit+",postStep="+postStep+",reason="+suspendReason());
return isSilent;
}
// iterate through our display list entries
void processDisplay(StringBuilder sb)
{
int count = displayCount();
for(int i=0;i<count; i++)
{
DisplayAction a = displayAt(i);
if (a.isEnabled())
{
try
{
sb.append(a.getId());
sb.append(": "); //$NON-NLS-1$
sb.append(a.getContent());
sb.append(" = "); //$NON-NLS-1$
// command[0] contains our expression, so first we parse it, evalulate it then print it
Object result = m_exprCache.evaluate(a.getExpression(), a.getIsolateId()).value;
if (result instanceof Variable)
ExpressionCache.appendVariableValue(sb, ((Variable)result).getValue(), a.getIsolateId());
else if (result instanceof Value)
ExpressionCache.appendVariableValue(sb, (Value) result, a.getIsolateId());
else if (result instanceof InternalProperty)
sb.append( ((InternalProperty)result).valueOf() );
else
sb.append(result);
sb.append(m_newline);
}
catch(NoSuchVariableException nsv)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("variable", nsv.getMessage() ); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("variableUnknown", args)); //$NON-NLS-1$
sb.append(m_newline);
}
catch(NumberFormatException nfe)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("value", nfe.getMessage() ); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("couldNotConvertToNumber", args)); //$NON-NLS-1$
sb.append(m_newline);
}
catch(PlayerFaultException pfe)
{
sb.append(pfe.getMessage() + m_newline);
}
catch (PlayerDebugException e)
{
sb.append(e.getMessage() + m_newline);
}
catch(NullPointerException npe)
{
sb.append(getLocalizationManager().getLocalizedTextString("couldNotEvaluate")); //$NON-NLS-1$
}
}
}
}
/**
* Determines if the given BreakAction requests a halt given the file
* line and optionally a conditional to evaluate.'
*/
boolean shouldBreak(BreakAction a, int fileId, int line, int isolateId)
{
boolean should = a.isEnabled();
ValueExp exp = a.getCondition();
if (should && exp != null && !getRequestHalt(isolateId)) // halt request fires true
{
// evaluate it then update our boolean
try
{
EvaluationResult result = evalExpression(exp, false, isolateId);
if (result != null)
should = ECMA.toBoolean(result.context.toValue(result.value));
}
catch(NullPointerException npe) {}
catch(NumberFormatException nfe) {}
}
return should;
}
/**
* Sets the command interpreter up to execute the
* given string a command
*
* This io redirection crap is really UGLY!!!
*/
void issueCommand(String cmd, StringBuilder output)
{
ByteArrayOutputStream ba = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(ba);
// temporarily re-wire i/o to catch all output
PrintStream oldOut = m_out;
PrintStream oldErr = m_err;
m_out = ps;
m_err = ps;
try
{
setCurrentLine(cmd);
processLine();
}
catch(AmbiguousException ae)
{
// we already put up a warning for the user
}
catch(IllegalStateException ise)
{
err(getLocalizationManager().getLocalizedTextString("illegalStateException")); //$NON-NLS-1$
}
catch(IllegalMonitorStateException ime)
{
err(getLocalizationManager().getLocalizedTextString("commandNotValidUntilPlayerSuspended")); //$NON-NLS-1$
}
catch(NoSuchElementException nse)
{
err(getLocalizationManager().getLocalizedTextString("noSuchElementException")); //$NON-NLS-1$
}
catch(SocketException se)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("socketErrorMessage", se.getMessage()); //$NON-NLS-1$
err(getLocalizationManager().getLocalizedTextString("problemWithConnection", args)); //$NON-NLS-1$
}
catch(Exception e)
{
err(getLocalizationManager().getLocalizedTextString("unexpectedErrorWithStackTrace")); //$NON-NLS-1$
if (Trace.error)
e.printStackTrace();
}
// flush the stream and then send its contents to our string buffer
ps.flush();
output.append( ba.toString() );
m_err = oldErr;
m_out = oldOut;
}
/**
* We have received a fault and are possibly suspended at this point.
* We need to look at our fault table and determine what do.
* @return true if we resumed execution
*/
boolean handleFault(FaultEvent e)
{
// lookup what we need to do
boolean requestResume = false;
String name = e.name();
boolean stop = true;
boolean print = true;
try
{
print = m_faultTable.is(name, "print"); //$NON-NLS-1$
stop = m_faultTable.is(name, "stop"); //$NON-NLS-1$
}
catch(NullPointerException npe)
{
if (Trace.error)
{
Map<String, Object> args = new HashMap<String, Object>();
args.put("faultName", name); //$NON-NLS-1$
Trace.trace(getLocalizationManager().getLocalizedTextString("faultHasNoTableEntry", args)); //$NON-NLS-1$
npe.printStackTrace();
}
}
if (e instanceof ExceptionFault)
{
ExceptionFault ef = (ExceptionFault) e;
Value thrownValue = ef.getThrownValue();
if (thrownValue != null)
{
if (!ef.willExceptionBeCaught())
{
stop = true;
}
else
{
stop = false;
String typeName = thrownValue.getTypeName();
int at = typeName.indexOf('@');
if (at != -1)
typeName = typeName.substring(0, at);
for (int i=0; i<catchpointCount(); ++i)
{
CatchAction c = catchpointAt(i);
String typeToCatch = c.getTypeToCatch();
try {
if (typeToCatch == null || getSession().getWorkerSession(e.isolateId).evalIs(thrownValue, typeToCatch))
{
stop = true;
break;
}
} catch (PlayerDebugException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
stop = true;
} catch (PlayerFaultException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
stop = true;
}
}
if (!stop)
print = false;
}
}
}
// should we stop?
if (!stop)
requestResume = true;
if (print)
dumpFaultLine(e);
return requestResume;
}
// wait a little bit of time until the player halts, if not throw an exception!
void waitTilHalted(int isolateId) throws NotConnectedException
{
if (!haveConnection())
throw new IllegalStateException();
int timeout = propertyGet(HALT_TIMEOUT);
int update = propertyGet(UPDATE_DELAY);
boolean wait = (propertyGet(NO_WAITING) == 1) ? false : true;
if (wait)
{
// spin for a while waiting for a halt; updating trace messages as we get them
waitForSuspend(timeout, update, isolateId);
if (!m_session.getWorkerSession(isolateId).isSuspended())
throw new IllegalMonitorStateException();
}
}
/**
* We spin in this spot until the player reaches the
* requested suspend state, either true or false.
*
* During this time we wake up every period milliseconds
* and update the display and our state with information
* received from the debug event queue.
*/
void waitForSuspend(int timeout, int period) throws NotConnectedException
{
waitForSuspend(timeout, period, Isolate.DEFAULT_ID);
}
/**
* We spin in this spot until the player reaches the
* requested suspend state, either true or false.
*
* During this time we wake up every period milliseconds
* and update the display and our state with information
* received from the debug event queue.
*/
void waitForSuspend(int timeout, int period, int isolateId) throws NotConnectedException
{
IsolateSession workerSession = m_session.getWorkerSession(isolateId);
while(timeout > 0)
{
// dump our events to the console while we are waiting.
processEvents();
if (workerSession.isSuspended())
break;
try { Thread.sleep(period); } catch(InterruptedException ie) {}
timeout -= period;
}
}
/**
* If we still have a socket try to send an exit message
* Doesn't seem to work ?!?
*/
void exitSession()
{
// clear out our watchpoint list and displays
// keep breakpoints around so that we can try to reapply them if we reconnect
m_displays.clear();
m_watchpoints.clear();
if (m_fileInfo != null)
m_fileInfo.unbind();
if (m_session != null)
m_session.terminate();
m_session = null;
m_fileInfo = null;
}
void initSession(Session s)
{
s.setSourceLocator(this);
m_fileInfo = new FileInfoCache();
m_exprCache.clear();
m_fileInfo.bind(s);
m_exprCache.bind(s);
// bind catching a version problem
boolean correctVersion = true;
try { s.bind(); } catch(VersionException ve) { correctVersion = false; }
// reset session properties
propertyPut(LIST_LINE, 1);
propertyPut(LIST_MODULE, 1); // default to module #1
propertyPut(BPNUM, 0); // set current breakpoint number as something bad
propertyPut(LAST_FRAME_DEPTH, 0);
propertyPut(CURRENT_FRAME_DEPTH, 0);
propertyPut(DISPLAY_FRAME_NUMBER, 0);
propertyPut(METADATA_ATTEMPTS_PERIOD, 250); // 1/4s per attempt
propertyPut(METADATA_NOT_AVAILABLE, 0); // counter for failures
propertyPut(METADATA_ATTEMPTS, METADATA_RETRIES);
propertyPut(PLAYER_FULL_SUPPORT, correctVersion ? 1 : 0);
String previousURI = m_mruURI;
m_mruURI = m_session.getURI();
// try to reapply the breakpoint's
if (previousURI != null && m_mruURI != null && previousURI.equalsIgnoreCase(m_mruURI))
reapplyBreakpoints();
else
{
while(m_breakpoints.size() > 0)
m_breakpoints.removeElementAt(0);
}
m_mainState.m_requestResume = false;
m_mainState.m_stepResume = false;
initIsolateState();
}
private void initIsolateState() {
m_activeIsolate = Isolate.DEFAULT_ID;
m_breakIsolates = new Vector<Integer>();
m_isolateState = new HashMap<Integer, DebugCLIIsolateState>();
m_isolateState.put(Isolate.DEFAULT_ID, m_mainState);
}
/**
* Walk through the list of breakpoints and try to apply them to our session
* We aren't that smart in that we ignore the singleSwf property of the breakpoint
* meaning that if you have a breakpoint set on a single swf, it will be restored
* across all swfs.
*/
void reapplyBreakpoints()
{
// give us a bit of time to process the newly loaded swf
if (propertyGet(METADATA_ATTEMPTS) > 0)
try { waitForMetaData(80); } catch(InProgressException ipe) { }
int count = breakpointCount();
for(int i=0; i<count; i++)
{
BreakAction a = breakpointAt(i);
a.clearHits();
a.setStatus(BreakAction.UNRESOLVED);
}
StringBuilder sb = new StringBuilder();
resolveBreakpoints(sb);
out(sb.toString());
}
/**
* Process a single line of input and return true if the quit command was encountered
*/
public boolean processLine() throws IOException, AmbiguousException, PlayerDebugException
{
if (!hasMoreTokens())
return false;
String command = nextToken();
boolean quit = false;
int cmdID = commandFor(command);
/* assume line will not be repeated. (i.e. user hits CR nothing happens) */
m_repeatLine = null;
switch(cmdID)
{
case CMD_QUIT:
quit = doQuit();
break;
case CMD_CONTINUE:
doContinue();
break;
case CMD_HOME:
doHome();
break;
case CMD_HELP:
doHelp();
break;
case CMD_SHOW:
doShow();
break;
case CMD_STEP:
doStep();
break;
case CMD_NEXT:
doNext();
break;
case CMD_FINISH:
doFinish();
break;
case CMD_BREAK:
doBreak();
break;
case CMD_CLEAR:
doClear();
break;
case CMD_SET:
doSet();
break;
case CMD_LIST:
doList();
break;
case CMD_PRINT:
doPrint();
break;
case CMD_TUTORIAL:
doTutorial();
break;
case CMD_INFO:
doInfo();
break;
case CMD_FILE:
doFile();
break;
case CMD_DELETE:
doDelete();
break;
case CMD_RUN:
doRun();
break;
case CMD_SOURCE:
doSource();
break;
case CMD_KILL:
doKill();
break;
case CMD_HANDLE:
doHandle();
break;
case CMD_ENABLE:
doEnable();
break;
case CMD_DISABLE:
doDisable();
break;
case CMD_DISPLAY:
doDisplay();
break;
case CMD_UNDISPLAY:
doUnDisplay();
break;
case CMD_COMMANDS:
doCommands();
break;
case CMD_PWD:
doPWD();
break;
case CMD_CF:
doCF();
break;
// case CMD_AWATCH:
// doWatch(true, true);
// break;
case CMD_WATCH:
doWatch(false, true);
break;
// case CMD_RWATCH:
// doWatch(true, false);
// break;
case CMD_CONDITION:
doCondition();
break;
case CMD_WHAT:
doWhat();
break;
case CMD_DISASSEMBLE:
doDisassemble();
break;
case CMD_HALT:
doHalt();
break;
case CMD_MCTREE:
doMcTree();
break;
case CMD_VIEW_SWF:
doViewSwf();
break;
case CMD_DOWN:
doDown();
break;
case CMD_UP:
doUp();
break;
case CMD_FRAME:
doFrame();
break;
case CMD_COMMENT:
; // nop
break;
case INFO_STACK_CMD:
; // from bt
doInfoStack();
break;
case CMD_DIRECTORY:
doDirectory();
break;
case CMD_CATCH:
doCatch();
break;
case CMD_CONNECT:
doConnect();
break;
case CMD_WORKER:
doWorker();
break;
default:
doUnknown(command);
break;
}
return quit;
}
void doWorker() throws IOException, NotSupportedException, NotSuspendedException, NoResponseException, NotConnectedException
{
if (hasMoreTokens()) {
String isolateid = nextToken();
if (isolateid != null && isolateid.length() > 0) {
try {
int id = Integer.parseInt(isolateid);
id++;
boolean found = false;
for (Isolate isolate : m_session.getWorkers()) {
if (isolate.getId() == id)
found = true;
}
StringBuilder sb = new StringBuilder();
if (found) {
m_activeIsolate = id;
propertyPut(LIST_WORKER, id);
propertyPut(LIST_LINE, 1);
propertyPut(LIST_MODULE, 1); // default to module #1
propertyPut(BPNUM, 0); // set current breakpoint number as something bad
propertyPut(LAST_FRAME_DEPTH, 0);
propertyPut(CURRENT_FRAME_DEPTH, 0);
propertyPut(DISPLAY_FRAME_NUMBER, 0);
sb.append(getLocalizationManager().getLocalizedTextString("workerChanged") + " "); //$NON-NLS-1$ //$NON-NLS-2$
if (id == Isolate.DEFAULT_ID) {
sb.append(getLocalizationManager().getLocalizedTextString("mainThread")); //$NON-NLS-1$
}
else
sb.append((id - 1));
sb.append(m_newline);
}
else {
sb.append(getLocalizationManager().getLocalizedTextString("workerNotFound") + " " + (id - 1)); //$NON-NLS-1$ //$NON-NLS-2$
sb.append(m_newline);
}
out(sb.toString());
}
catch(NumberFormatException e) {
err(e.getLocalizedMessage());
}
}
}
}
/**
* Read help text from fdbhelp*.txt.
*/
String getHelpTopic(String topic)
{
// Open the file fdbhelp*.txt that is a sibling of this class file.
// (Note: build.xml copies it into the classes directory.)
InputStream helpStream = Help.getResourceAsStream();
if (helpStream == null)
return getLocalizationManager().getLocalizedTextString("noHelpFileFound"); //$NON-NLS-1$
// Read the help file line-by-line, looking for topic lines like [Break].
// Build an array of the lines within the section for the specified topic.
topic = "[" + topic + "]"; //$NON-NLS-1$ //$NON-NLS-2$
Vector<String> lines = new Vector<String>();
BufferedReader r = null;
try
{
r = new BufferedReader(new InputStreamReader(helpStream, "UTF-8")); //$NON-NLS-1$
String line;
// Read lines until we find the specified topic line.
while ((line = r.readLine()) != null)
{
if (line.startsWith(topic))
break;
}
// Read lines until we find the next topic line.
while ((line = r.readLine()) != null)
{
if (line.startsWith("[")) //$NON-NLS-1$
break;
lines.add(line);
}
}
catch(FileNotFoundException fnf)
{
err(fnf.getLocalizedMessage());
}
catch(IOException e)
{
err(e.getLocalizedMessage());
}
finally
{
if (r != null)
try { r.close(); } catch (IOException e) { e.printStackTrace(); }
}
// Concatenate the lines, leaving out the first and last ones
// which are supposed to be blank. They're only there to make
// fdbhelp*.txt more readable.
StringBuilder helpText = new StringBuilder();
int n = lines.size();
for (int i = 1; i < n - 1; i++)
{
String line = lines.get(i);
helpText.append(line);
if (i != n - 2)
helpText.append(m_newline);
}
return helpText.toString();
}
/**
* Provide contenxt sensistive help
*/
void doHelp() throws AmbiguousException
{
// someone entered a help command so let's help them!
String topic = "help"; //$NON-NLS-1$
int cmd;
String commandName;
// they might have entered something like "help br"
if (hasMoreTokens())
{
// map "br" to CMD_BREAK
cmd = commandFor(nextToken());
// and then back to "break"
commandName = commandNumberToCommandName(g_commandArray, cmd);
// so we'll look up the topic named "break" in fdbhelp*.txt
topic = commandName;
// they might have entered something like "help inf fil"
if (cmd == CMD_INFO && hasMoreTokens())
{
// map "fil" to CMD_INFO_FILE
cmd = infoCommandFor(nextToken());
// and then back to "file"
commandName = commandNumberToCommandName(g_infoCommandArray, cmd);
// so we'll look up the topic named "info file" in fdbhelp*.txt
topic += " " + commandName; //$NON-NLS-1$
}
// or like "help sho n"
else if (cmd == CMD_SHOW && hasMoreTokens())
{
// map "n" to CMD_SHOW_NET
cmd = showCommandFor(nextToken());
// and then back to "net"
commandName = commandNumberToCommandName(g_showCommandArray, cmd);
// so we'll look up the topic named "show net" in fdbhelp*.txt
topic += " " + commandName; //$NON-NLS-1$
}
}
out( getHelpTopic(topic) );
}
void doTutorial()
{
out( getHelpTopic("Tutorial") ); //$NON-NLS-1$
}
// process strings to command ids
int commandFor(String s) throws AmbiguousException { return determineCommand(g_commandArray, s, CMD_UNKNOWN); }
int showCommandFor(String s) throws AmbiguousException { return determineCommand(g_showCommandArray, s, SHOW_UNKNOWN_CMD); }
int infoCommandFor(String s) throws AmbiguousException { return determineCommand(g_infoCommandArray, s, INFO_UNKNOWN_CMD); }
int enableCommandFor(String s) throws AmbiguousException { return determineCommand(g_enableCommandArray, s, CMD_UNKNOWN); }
int disableCommandFor(String s) throws AmbiguousException { return determineCommand(g_disableCommandArray, s, CMD_UNKNOWN); }
/**
* Attempt to match given the given string against our set of commands
* @return the command code that was hit.
*/
int determineCommand(StringIntArray cmdList, String input, int defCmd) throws AmbiguousException
{
int cmd = defCmd;
// first check for a comment
if (input.charAt(0) == '#')
cmd = CMD_COMMENT;
else
{
// long start = System.currentTimeMillis();
ArrayList ar = cmdList.elementsStartingWith(input);
// long end = System.currentTimeMillis();
int size = ar.size();
/**
* 3 cases:
* - No hits, return unknown and let our caller
* dump the error.
* - We match unambiguously or we have 1 or more matches
* and the input is a single character. We then take the
* first hit as our command.
* - If we have multiple hits then we dump a 'ambiguous' message
* and puke quietly.
*/
if (size == 0)
; // no command match return unknown
// only 1 match or our input is 1 character or first match is exact
else if (size == 1 ||
input.length() == 1 ||
cmdList.getString( ((Integer)ar.get(0)).intValue() ).compareTo(input) == 0)
{
cmd = (cmdList.getInteger( ((Integer)ar.get(0)).intValue() )).intValue();
}
else
{
// matches more than one command dump message and go
StringBuilder sb = new StringBuilder();
Map<String, Object> args = new HashMap<String, Object>();
args.put("input", input); //$NON-NLS-1$
sb.append(getLocalizationManager().getLocalizedTextString("ambiguousCommand", args)); //$NON-NLS-1$
sb.append(' ');
sb.append(input);
for(int i=0; i<size; i++)
{
String s = cmdList.getString( ((Integer)ar.get(i)).intValue() );
sb.append(s);
if (i+1 < size)
sb.append(", "); //$NON-NLS-1$
}
sb.append('.');
err( sb.toString() );
throw new AmbiguousException();
}
}
return cmd;
}
String commandNumberToCommandName(StringIntArray cmdList, int cmdNumber)
{
for (int i = 0; i < cmdList.size(); i++)
{
if (cmdList.getInt(i) == cmdNumber)
return cmdList.getString(i);
}
return "?"; //$NON-NLS-1$
}
/**
* The array of top level commands that we support.
* They are placed into a Nx2 array, whereby the first component
* is a String which is the command and the 2nd component is the
* integer identifier for the command.
*
* The StringIntArray object provides a convenient wrapper class
* that implements the List interface.
*
* NOTE: order matters! For the case of a single character
* match, we let the first hit act like an unambiguous match.
*/
static StringIntArray g_commandArray = new StringIntArray( new Object[][]
{
{ "awatch", new Integer(CMD_AWATCH) }, //$NON-NLS-1$
{ "break", new Integer(CMD_BREAK) }, //$NON-NLS-1$
{ "bt", new Integer(INFO_STACK_CMD) }, //$NON-NLS-1$
{ "continue", new Integer(CMD_CONTINUE) }, //$NON-NLS-1$
{ "catch", new Integer(CMD_CATCH) }, //$NON-NLS-1$
{ "cf", new Integer(CMD_CF) }, //$NON-NLS-1$
{ "clear", new Integer(CMD_CLEAR) }, //$NON-NLS-1$
{ "commands", new Integer(CMD_COMMANDS) }, //$NON-NLS-1$
{ "condition", new Integer(CMD_CONDITION) }, //$NON-NLS-1$
{ "connect", new Integer(CMD_CONNECT) }, //$NON-NLS-1$
{ "delete", new Integer(CMD_DELETE) }, //$NON-NLS-1$
{ "disable", new Integer(CMD_DISABLE) }, //$NON-NLS-1$
{ "disassemble", new Integer(CMD_DISASSEMBLE) }, //$NON-NLS-1$
{ "display", new Integer(CMD_DISPLAY) }, //$NON-NLS-1$
{ "directory", new Integer(CMD_DIRECTORY) }, //$NON-NLS-1$
{ "down", new Integer(CMD_DOWN) }, //$NON-NLS-1$
{ "enable", new Integer(CMD_ENABLE) }, //$NON-NLS-1$
{ "finish", new Integer(CMD_FINISH) }, //$NON-NLS-1$
{ "file", new Integer(CMD_FILE) }, //$NON-NLS-1$
{ "frame", new Integer(CMD_FRAME) }, //$NON-NLS-1$
{ "help", new Integer(CMD_HELP) }, //$NON-NLS-1$
{ "halt", new Integer(CMD_HALT) }, //$NON-NLS-1$
{ "handle", new Integer(CMD_HANDLE) }, //$NON-NLS-1$
{ "home", new Integer(CMD_HOME) }, //$NON-NLS-1$
{ "info", new Integer(CMD_INFO) }, //$NON-NLS-1$
{ "kill", new Integer(CMD_KILL) }, //$NON-NLS-1$
{ "list", new Integer(CMD_LIST) }, //$NON-NLS-1$
{ "next", new Integer(CMD_NEXT) }, //$NON-NLS-1$
{ "nexti", new Integer(CMD_NEXT) }, //$NON-NLS-1$
{ "mctree", new Integer(CMD_MCTREE) }, //$NON-NLS-1$
{ "print", new Integer(CMD_PRINT) }, //$NON-NLS-1$
{ "pwd", new Integer(CMD_PWD) }, //$NON-NLS-1$
{ "quit", new Integer(CMD_QUIT) }, //$NON-NLS-1$
{ "run", new Integer(CMD_RUN) }, //$NON-NLS-1$
{ "rwatch", new Integer(CMD_RWATCH) }, //$NON-NLS-1$
{ "step", new Integer(CMD_STEP) }, //$NON-NLS-1$
{ "stepi", new Integer(CMD_STEP) }, //$NON-NLS-1$
{ "set", new Integer(CMD_SET) }, //$NON-NLS-1$
{ "show", new Integer(CMD_SHOW) }, //$NON-NLS-1$
{ "source", new Integer(CMD_SOURCE) }, //$NON-NLS-1$
{ "tutorial", new Integer(CMD_TUTORIAL) }, //$NON-NLS-1$
{ "undisplay", new Integer(CMD_UNDISPLAY) }, //$NON-NLS-1$
{ "up", new Integer(CMD_UP) }, //$NON-NLS-1$
{ "where", new Integer(INFO_STACK_CMD) }, //$NON-NLS-1$
{ "watch", new Integer(CMD_WATCH) }, //$NON-NLS-1$
{ "what", new Integer(CMD_WHAT) }, //$NON-NLS-1$
{ "viewswf", new Integer(CMD_VIEW_SWF) }, //$NON-NLS-1$
{ "worker", new Integer(CMD_WORKER) }, //$NON-NLS-1$
} );
/**
* Info sub-commands
*/
static StringIntArray g_infoCommandArray = new StringIntArray( new Object[][]
{
{ "arguments", new Integer(INFO_ARGS_CMD) }, //$NON-NLS-1$
{ "breakpoints", new Integer(INFO_BREAK_CMD) }, //$NON-NLS-1$
{ "display", new Integer(INFO_DISPLAY_CMD) }, //$NON-NLS-1$
{ "files", new Integer(INFO_FILES_CMD) }, //$NON-NLS-1$
{ "functions", new Integer(INFO_FUNCTIONS_CMD) }, //$NON-NLS-1$
{ "handle", new Integer(INFO_HANDLE_CMD) }, //$NON-NLS-1$
{ "locals", new Integer(INFO_LOCALS_CMD) }, //$NON-NLS-1$
{ "stack", new Integer(INFO_STACK_CMD) }, //$NON-NLS-1$
{ "scopechain", new Integer(INFO_SCOPECHAIN_CMD) }, //$NON-NLS-1$
{ "sources", new Integer(INFO_SOURCES_CMD) }, //$NON-NLS-1$
{ "swfs", new Integer(INFO_SWFS_CMD) }, //$NON-NLS-1$
{ "targets", new Integer(INFO_TARGETS_CMD) }, //$NON-NLS-1$
{ "variables", new Integer(INFO_VARIABLES_CMD) }, //$NON-NLS-1$
{ "workers", new Integer(INFO_WORKERS_CMD) }, //$NON-NLS-1$
} );
/**
* Show sub-commands
*/
static StringIntArray g_showCommandArray = new StringIntArray( new Object[][]
{
{ "break", new Integer(SHOW_BREAK_CMD) }, //$NON-NLS-1$
{ "directories", new Integer(SHOW_DIRS_CMD) }, //$NON-NLS-1$
{ "files", new Integer(SHOW_FILES_CMD) }, //$NON-NLS-1$
{ "functions", new Integer(SHOW_FUNC_CMD) }, //$NON-NLS-1$
{ "locations", new Integer(SHOW_LOC_CMD) }, //$NON-NLS-1$
{ "memory", new Integer(SHOW_MEM_CMD) }, //$NON-NLS-1$
{ "net", new Integer(SHOW_NET_CMD) }, //$NON-NLS-1$
{ "properties", new Integer(SHOW_PROPERTIES_CMD) }, //$NON-NLS-1$
{ "uri", new Integer(SHOW_URI_CMD) }, //$NON-NLS-1$
{ "variable", new Integer(SHOW_VAR_CMD) }, //$NON-NLS-1$
} );
/**
* enable sub-commands
*/
static StringIntArray g_enableCommandArray = new StringIntArray( new Object[][]
{
{ "breakpoints", new Integer(CMD_BREAK) }, //$NON-NLS-1$
{ "display", new Integer(CMD_DISPLAY) }, //$NON-NLS-1$
{ "delete", new Integer(CMD_DELETE) }, //$NON-NLS-1$
{ "once", new Integer(ENABLE_ONCE_CMD) }, //$NON-NLS-1$
} );
/**
* disable sub-commands
*/
static StringIntArray g_disableCommandArray = new StringIntArray( new Object[][]
{
{ "display", new Integer(CMD_DISPLAY) }, //$NON-NLS-1$
{ "breakpoints", new Integer(CMD_BREAK) }, //$NON-NLS-1$
} );
/**
* -------------------------------------------------------------------------
* Any code that accesses the implementation of the API is wrapped
* in Extensions. This way one can easily factor this stuff out
* and build an fdb that is completely compliant to the API.
*
* I'm pretty sure there's a better way of doing this like
* making Extensions a final static variable and then
* toggling it between two classes Extensions and something
* like ExtensionsDisabled (methods with only out("not supported")
* in them).
* -------------------------------------------------------------------------
*/
void appendBreakInfo(StringBuilder sb, int isolateId) throws NotConnectedException { Extensions.appendBreakInfo(this, sb, false, isolateId); }
void doShowStats() { Extensions.doShowStats(this); }
void doShowFuncs() { Extensions.doShowFuncs(this); }
void doShowProperties() { Extensions.doShowProperties(this); }
void doShowVariable() throws PlayerDebugException { Extensions.doShowVariable(this); }
void doShowBreak() throws NotConnectedException { Extensions.doShowBreak(this); }
void doDisassemble() throws PlayerDebugException { Extensions.doDisassemble(this); }
}