J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1998-1999 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package com.sun.tools.example.debug.gui; |
| 27 | |
| 28 | import java.io.*; |
| 29 | import java.util.*; |
| 30 | |
| 31 | import com.sun.jdi.*; |
| 32 | import com.sun.tools.example.debug.event.*; |
| 33 | import com.sun.tools.example.debug.bdi.*; |
| 34 | |
| 35 | public class ContextManager { |
| 36 | |
| 37 | private ClassManager classManager; |
| 38 | private ExecutionManager runtime; |
| 39 | |
| 40 | private String mainClassName; |
| 41 | private String vmArguments; |
| 42 | private String commandArguments; |
| 43 | private String remotePort; |
| 44 | |
| 45 | private ThreadReference currentThread; |
| 46 | |
| 47 | private boolean verbose; |
| 48 | |
| 49 | private Vector<ContextListener> contextListeners = new Vector<ContextListener>(); |
| 50 | |
| 51 | public ContextManager(Environment env) { |
| 52 | classManager = env.getClassManager(); |
| 53 | runtime = env.getExecutionManager(); |
| 54 | mainClassName = ""; |
| 55 | vmArguments = ""; |
| 56 | commandArguments = ""; |
| 57 | currentThread = null; |
| 58 | |
| 59 | ContextManagerListener listener = new ContextManagerListener(); |
| 60 | runtime.addJDIListener(listener); |
| 61 | runtime.addSessionListener(listener); |
| 62 | } |
| 63 | |
| 64 | // Program execution defaults. |
| 65 | |
| 66 | //### Should there be change listeners for these? |
| 67 | //### They would be needed if we expected a dialog to be |
| 68 | //### synchronized with command input while it was open. |
| 69 | |
| 70 | public String getMainClassName() { |
| 71 | return mainClassName; |
| 72 | } |
| 73 | |
| 74 | public void setMainClassName(String mainClassName) { |
| 75 | this.mainClassName = mainClassName; |
| 76 | } |
| 77 | |
| 78 | public String getVmArguments() { |
| 79 | return processClasspathDefaults(vmArguments); |
| 80 | } |
| 81 | |
| 82 | public void setVmArguments(String vmArguments) { |
| 83 | this.vmArguments = vmArguments; |
| 84 | } |
| 85 | |
| 86 | public String getProgramArguments() { |
| 87 | return commandArguments; |
| 88 | } |
| 89 | |
| 90 | public void setProgramArguments(String commandArguments) { |
| 91 | this.commandArguments = commandArguments; |
| 92 | } |
| 93 | |
| 94 | public String getRemotePort() { |
| 95 | return remotePort; |
| 96 | } |
| 97 | |
| 98 | public void setRemotePort(String remotePort) { |
| 99 | this.remotePort = remotePort; |
| 100 | |
| 101 | } |
| 102 | |
| 103 | |
| 104 | // Miscellaneous debugger session preferences. |
| 105 | |
| 106 | public boolean getVerboseFlag() { |
| 107 | return verbose; |
| 108 | } |
| 109 | |
| 110 | public void setVerboseFlag(boolean verbose) { |
| 111 | this.verbose = verbose; |
| 112 | } |
| 113 | |
| 114 | |
| 115 | // Thread focus. |
| 116 | |
| 117 | public ThreadReference getCurrentThread() { |
| 118 | return currentThread; |
| 119 | } |
| 120 | |
| 121 | public void setCurrentThread(ThreadReference t) { |
| 122 | if (t != currentThread) { |
| 123 | currentThread = t; |
| 124 | notifyCurrentThreadChanged(t); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | public void setCurrentThreadInvalidate(ThreadReference t) { |
| 129 | currentThread = t; |
| 130 | notifyCurrentFrameChanged(runtime.threadInfo(t), |
| 131 | 0, true); |
| 132 | } |
| 133 | |
| 134 | public void invalidateCurrentThread() { |
| 135 | notifyCurrentFrameChanged(null, 0, true); |
| 136 | } |
| 137 | |
| 138 | |
| 139 | // If a view is displaying the current thread, it may |
| 140 | // choose to indicate which frame is current in the |
| 141 | // sense of the command-line UI. It may also "warp" the |
| 142 | // selection to that frame when changed by an 'up' or 'down' |
| 143 | // command. Hence, a notifier is provided. |
| 144 | |
| 145 | /****** |
| 146 | public int getCurrentFrameIndex() { |
| 147 | return getCurrentFrameIndex(currentThreadInfo); |
| 148 | } |
| 149 | ******/ |
| 150 | |
| 151 | public int getCurrentFrameIndex(ThreadReference t) { |
| 152 | return getCurrentFrameIndex(runtime.threadInfo(t)); |
| 153 | } |
| 154 | |
| 155 | //### Used in StackTraceTool. |
| 156 | public int getCurrentFrameIndex(ThreadInfo tinfo) { |
| 157 | if (tinfo == null) { |
| 158 | return 0; |
| 159 | } |
| 160 | Integer currentFrame = (Integer)tinfo.getUserObject(); |
| 161 | if (currentFrame == null) { |
| 162 | return 0; |
| 163 | } else { |
| 164 | return currentFrame.intValue(); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | public int moveCurrentFrameIndex(ThreadReference t, int count) throws VMNotInterruptedException { |
| 169 | return setCurrentFrameIndex(t,count, true); |
| 170 | } |
| 171 | |
| 172 | public int setCurrentFrameIndex(ThreadReference t, int newIndex) throws VMNotInterruptedException { |
| 173 | return setCurrentFrameIndex(t, newIndex, false); |
| 174 | } |
| 175 | |
| 176 | public int setCurrentFrameIndex(int newIndex) throws VMNotInterruptedException { |
| 177 | if (currentThread == null) { |
| 178 | return 0; |
| 179 | } else { |
| 180 | return setCurrentFrameIndex(currentThread, newIndex, false); |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | private int setCurrentFrameIndex(ThreadReference t, int x, boolean relative) throws VMNotInterruptedException { |
| 185 | boolean sameThread = t.equals(currentThread); |
| 186 | ThreadInfo tinfo = runtime.threadInfo(t); |
| 187 | if (tinfo == null) { |
| 188 | return 0; |
| 189 | } |
| 190 | int maxIndex = tinfo.getFrameCount()-1; |
| 191 | int oldIndex = getCurrentFrameIndex(tinfo); |
| 192 | int newIndex = relative? oldIndex + x : x; |
| 193 | if (newIndex > maxIndex) { |
| 194 | newIndex = maxIndex; |
| 195 | } else if (newIndex < 0) { |
| 196 | newIndex = 0; |
| 197 | } |
| 198 | if (!sameThread || newIndex != oldIndex) { // don't recurse |
| 199 | setCurrentFrameIndex(tinfo, newIndex); |
| 200 | } |
| 201 | return newIndex - oldIndex; |
| 202 | } |
| 203 | |
| 204 | private void setCurrentFrameIndex(ThreadInfo tinfo, int index) { |
| 205 | tinfo.setUserObject(new Integer(index)); |
| 206 | //### In fact, the value may not have changed at this point. |
| 207 | //### We need to signal that the user attempted to change it, |
| 208 | //### however, so that the selection can be "warped" to the |
| 209 | //### current location. |
| 210 | notifyCurrentFrameChanged(tinfo.thread(), index); |
| 211 | } |
| 212 | |
| 213 | public StackFrame getCurrentFrame() throws VMNotInterruptedException { |
| 214 | return getCurrentFrame(runtime.threadInfo(currentThread)); |
| 215 | } |
| 216 | |
| 217 | public StackFrame getCurrentFrame(ThreadReference t) throws VMNotInterruptedException { |
| 218 | return getCurrentFrame(runtime.threadInfo(t)); |
| 219 | } |
| 220 | |
| 221 | public StackFrame getCurrentFrame(ThreadInfo tinfo) throws VMNotInterruptedException { |
| 222 | int index = getCurrentFrameIndex(tinfo); |
| 223 | try { |
| 224 | // It is possible, though unlikely, that the VM was interrupted |
| 225 | // before the thread created its Java stack. |
| 226 | return tinfo.getFrame(index); |
| 227 | } catch (FrameIndexOutOfBoundsException e) { |
| 228 | return null; |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | public void addContextListener(ContextListener cl) { |
| 233 | contextListeners.add(cl); |
| 234 | } |
| 235 | |
| 236 | public void removeContextListener(ContextListener cl) { |
| 237 | contextListeners.remove(cl); |
| 238 | } |
| 239 | |
| 240 | //### These notifiers are fired only in response to USER-INITIATED changes |
| 241 | //### to the current thread and current frame. When the current thread is set automatically |
| 242 | //### after a breakpoint hit or step completion, no event is generated. Instead, |
| 243 | //### interested parties are expected to listen for the BreakpointHit and StepCompleted |
| 244 | //### events. This convention is unclean, and I believe that it reflects a defect in |
| 245 | //### in the current architecture. Unfortunately, however, we cannot guarantee the |
| 246 | //### order in which various listeners receive a given event, and the handlers for |
| 247 | //### the very same events that cause automatic changes to the current thread may also |
| 248 | //### need to know the current thread. |
| 249 | |
| 250 | private void notifyCurrentThreadChanged(ThreadReference t) { |
| 251 | ThreadInfo tinfo = null; |
| 252 | int index = 0; |
| 253 | if (t != null) { |
| 254 | tinfo = runtime.threadInfo(t); |
| 255 | index = getCurrentFrameIndex(tinfo); |
| 256 | } |
| 257 | notifyCurrentFrameChanged(tinfo, index, false); |
| 258 | } |
| 259 | |
| 260 | private void notifyCurrentFrameChanged(ThreadReference t, int index) { |
| 261 | notifyCurrentFrameChanged(runtime.threadInfo(t), |
| 262 | index, false); |
| 263 | } |
| 264 | |
| 265 | private void notifyCurrentFrameChanged(ThreadInfo tinfo, int index, |
| 266 | boolean invalidate) { |
| 267 | Vector l = (Vector)contextListeners.clone(); |
| 268 | CurrentFrameChangedEvent evt = |
| 269 | new CurrentFrameChangedEvent(this, tinfo, index, invalidate); |
| 270 | for (int i = 0; i < l.size(); i++) { |
| 271 | ((ContextListener)l.elementAt(i)).currentFrameChanged(evt); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | private class ContextManagerListener extends JDIAdapter |
| 276 | implements SessionListener, JDIListener { |
| 277 | |
| 278 | // SessionListener |
| 279 | |
| 280 | public void sessionStart(EventObject e) { |
| 281 | invalidateCurrentThread(); |
| 282 | } |
| 283 | |
| 284 | public void sessionInterrupt(EventObject e) { |
| 285 | setCurrentThreadInvalidate(currentThread); |
| 286 | } |
| 287 | |
| 288 | public void sessionContinue(EventObject e) { |
| 289 | invalidateCurrentThread(); |
| 290 | } |
| 291 | |
| 292 | // JDIListener |
| 293 | |
| 294 | public void locationTrigger(LocationTriggerEventSet e) { |
| 295 | setCurrentThreadInvalidate(e.getThread()); |
| 296 | } |
| 297 | |
| 298 | public void exception(ExceptionEventSet e) { |
| 299 | setCurrentThreadInvalidate(e.getThread()); |
| 300 | } |
| 301 | |
| 302 | public void vmDisconnect(VMDisconnectEventSet e) { |
| 303 | invalidateCurrentThread(); |
| 304 | } |
| 305 | |
| 306 | } |
| 307 | |
| 308 | |
| 309 | /** |
| 310 | * Add a -classpath argument to the arguments passed to the exec'ed |
| 311 | * VM with the contents of CLASSPATH environment variable, |
| 312 | * if -classpath was not already specified. |
| 313 | * |
| 314 | * @param javaArgs the arguments to the VM being exec'd that |
| 315 | * potentially has a user specified -classpath argument. |
| 316 | * @return a javaArgs whose -classpath option has been added |
| 317 | */ |
| 318 | |
| 319 | private String processClasspathDefaults(String javaArgs) { |
| 320 | if (javaArgs.indexOf("-classpath ") == -1) { |
| 321 | StringBuffer munged = new StringBuffer(javaArgs); |
| 322 | SearchPath classpath = classManager.getClassPath(); |
| 323 | if (classpath.isEmpty()) { |
| 324 | String envcp = System.getProperty("env.class.path"); |
| 325 | if ((envcp != null) && (envcp.length() > 0)) { |
| 326 | munged.append(" -classpath " + envcp); |
| 327 | } |
| 328 | } else { |
| 329 | munged.append(" -classpath " + classpath.asString()); |
| 330 | } |
| 331 | return munged.toString(); |
| 332 | } else { |
| 333 | return javaArgs; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | private String appendPath(String path1, String path2) { |
| 338 | if (path1 == null || path1.length() == 0) { |
| 339 | return path2 == null ? "." : path2; |
| 340 | } else if (path2 == null || path2.length() == 0) { |
| 341 | return path1; |
| 342 | } else { |
| 343 | return path1 + File.pathSeparator + path2; |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | } |