http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/07f5a7de/debugger/src/main/java/flex/tools/debugger/cli/DebugCLI.java ---------------------------------------------------------------------- diff --git a/debugger/src/main/java/flex/tools/debugger/cli/DebugCLI.java b/debugger/src/main/java/flex/tools/debugger/cli/DebugCLI.java new file mode 100644 index 0000000..58ce94c --- /dev/null +++ b/debugger/src/main/java/flex/tools/debugger/cli/DebugCLI.java @@ -0,0 +1,6878 @@ +/* + * + * 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 com.sun.org.apache.xml.internal.utils.LocaleUtility; +import flash.localization.LocalizationManager; +import flash.tools.debugger.*; +import flash.tools.debugger.concrete.DProtocol; +import flash.tools.debugger.concrete.DSwfInfo; +import flash.tools.debugger.events.*; +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; + +import java.io.*; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.*; + +/** + * 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; + + /** + * Whether the break command will show it changed workers. + * true at the moment because the break function itself does not display in + * which worker the breakpoint has been set. + */ + public static final boolean WORKER_DISPLAY_INTERNAL_SWAP_INFO = true; + + /* 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; + private boolean m_quietMode; // Don't dump out the state of the execution when WORKER_DISPLAY_INTERNAL_SWAP_INFO is set to false. + 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; + private boolean m_isIde; + + /** + * 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$ + + 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; + } + + public boolean isIde() { + return m_isIde; + } + + /** + * 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 if (arg.equals("-ide")) //$NON-NLS-1$ + { + m_isIde = true; + } else if (arg.equals("-lang")) //$NON-NLS-1$ + { + if (i + 1 < args.length) + getLocalizationManager().setLocale(LocaleUtility.langToLocale(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, true); + 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) { + m_exprCache.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('='); + m_exprCache.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)) { + m_exprCache.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++) { + m_exprCache.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)) { + m_exprCache.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]; + m_exprCache.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(); + } + } + + 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 { + if (hasMoreTokens()) { + arg = nextToken(); + int id = arg.equals(".") ? propertyGet(LIST_MODULE) : parseFileArg(m_activeIsolate, -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()); + } + } + + 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$ + + // our list of breakpoints + 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(); + final LocationCollection locations = b.getLocations(); + 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); + } + + final StringBuilder workerList = new StringBuilder(); + if (locations != null) { + for (Iterator<Location> iterator = locations.iterator(); iterator.hasNext(); ) { + Location location = iterator.next(); + workerList.append(location.getIsolateId() - 1); + if (iterator.hasNext()) + workerList.append(" / "); + } + } + + + if (l != null) { + Map<String, Object> workerArgs = new HashMap<String, Object>(); + workerArgs.put("worker", workerList.toString()); //$NON-NLS-1$ + sb.append(" ("); + sb.append(getLocalizationManager().getLocalizedTextString("inWorker", workerArgs)); //$NON-NLS-1$ + sb.append(") "); + } + + 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, SuspendedException, NoResponseException, NotSupportedException, NotSuspendedException, IOException { + // 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 (!m_quietMode && 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 if (!m_quietMode) { + // very bad, set stepping so that we don't trigger a continue on a breakpoint or fault + out(getLocalizationManager().getLocalizedTextString("playerDidNotStop")); //$NON-NLS-1$ + } + m_quietMode = false; + } + } + + 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) { + 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 (reason == SuspendReason.Breakpoint && enabledBreakpointIndexOf(l) == -1) { + // Dont output anything + + // That's a edge case when a function breaks often, let's say a timer, + // if the break action is disabled, the break event are still dispatched, + // sent to the debugger and outputed, the debugger can't then halt the player anymore, + // even removing the output, that's the same, I will try to fix this better later, + // in between, I comment my changes. + }*/ 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); + + final String stackTrace = e.stackTrace(); + if (stackTrace != null && stackTrace.length() > 0) { + sb.append("\n").append(stackTrace); + } + } 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); + } + + final String stackTrace = e.stackTrace(); + if (stackTrace != null && stackTrace.length() > 0) { + sb.append("\n").append(stackTrace); + } + } + 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 > 0) ? 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; + if (!bp.isPropagable()) + 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); + else newLoc.getFile(); + + dumpAddedBreakpoint(bp); + + } 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$ + } + } + } + } + + private void dumpAddedBreakpoint(BreakAction bp) { + Location l = bp.getLocations().last(); + int which = bp.getId(); + String name = l.getFile().getName(); + int offset = adjustOffsetForUnitTests(l.getFile().getOffsetForLine(l.getLine())); + + 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(l.getLine())); //$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)); + } + + /** + * 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, SuspendedException, NotSupportedException, NotSuspendedException, IOException { + 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 && pendingPromptIsolate == m_activeIsolate) { + 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, SuspendedException, IOException, NotSupportedException, NotSuspendedException, NoResponseException { + 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
<TRUNCATED>
