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