1 /*
2  * Copyright (c) 1998, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * This source code is provided to illustrate the usage of a given feature
28  * or technique and has been deliberately simplified. Additional steps
29  * required for a production-quality application, such as security checks,
30  * input validation and proper error handling, might not be present in
31  * this sample code.
32  */
33 
34 
35 package com.sun.tools.example.debug.tty;
36 
37 import com.sun.jdi.*;
38 import com.sun.jdi.connect.Connector;
39 import com.sun.jdi.request.*;
40 import com.sun.tools.example.debug.expr.ExpressionParser;
41 import com.sun.tools.example.debug.expr.ParseException;
42 
43 import java.text.*;
44 import java.util.*;
45 import java.io.*;
46 
47 class Commands {
48 
49     abstract class AsyncExecution {
action()50         abstract void action();
51 
AsyncExecution()52         AsyncExecution() {
53             execute();
54         }
55 
execute()56         void execute() {
57             /*
58              * Save current thread and stack frame. (BugId 4296031)
59              */
60             final ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
61             final int stackFrame = threadInfo == null? 0 : threadInfo.getCurrentFrameIndex();
62             Thread thread = new Thread("asynchronous jdb command") {
63                     @Override
64                     public void run() {
65                         try {
66                             action();
67                         } catch (UnsupportedOperationException uoe) {
68                             //(BugId 4453329)
69                             MessageOutput.println("Operation is not supported on the target VM");
70                         } catch (Exception e) {
71                             MessageOutput.println("Internal exception during operation:",
72                                                   e.getMessage());
73                         } finally {
74                             /*
75                              * This was an asynchronous command.  Events may have been
76                              * processed while it was running.  Restore the thread and
77                              * stack frame the user was looking at.  (BugId 4296031)
78                              */
79                             if (threadInfo != null) {
80                                 ThreadInfo.setCurrentThreadInfo(threadInfo);
81                                 try {
82                                     threadInfo.setCurrentFrameIndex(stackFrame);
83                                 } catch (IncompatibleThreadStateException e) {
84                                     MessageOutput.println("Current thread isnt suspended.");
85                                 } catch (ArrayIndexOutOfBoundsException e) {
86                                     MessageOutput.println("Requested stack frame is no longer active:",
87                                                           new Object []{stackFrame});
88                                 }
89                             }
90                             MessageOutput.printPrompt();
91                         }
92                     }
93                 };
94             thread.start();
95         }
96     }
97 
Commands()98     Commands() {
99     }
100 
evaluate(String expr)101     private Value evaluate(String expr) {
102         Value result = null;
103         ExpressionParser.GetFrame frameGetter = null;
104         try {
105             final ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
106             if ((threadInfo != null) && (threadInfo.getCurrentFrame() != null)) {
107                 frameGetter = new ExpressionParser.GetFrame() {
108                         @Override
109                         public StackFrame get() throws IncompatibleThreadStateException {
110                             return threadInfo.getCurrentFrame();
111                         }
112                     };
113             }
114             result = ExpressionParser.evaluate(expr, Env.vm(), frameGetter);
115         } catch (InvocationException ie) {
116             MessageOutput.println("Exception in expression:",
117                                   ie.exception().referenceType().name());
118         } catch (Exception ex) {
119             String exMessage = ex.getMessage();
120             if (exMessage == null) {
121                 MessageOutput.printException(exMessage, ex);
122             } else {
123                 String s;
124                 try {
125                     s = MessageOutput.format(exMessage);
126                 } catch (MissingResourceException mex) {
127                     s = ex.toString();
128                 }
129                 MessageOutput.printDirectln(s);// Special case: use printDirectln()
130             }
131         }
132         return result;
133     }
134 
getStringValue()135     private String getStringValue() {
136          Value val = null;
137          String valStr = null;
138          try {
139               val = ExpressionParser.getMassagedValue();
140               valStr = val.toString();
141          } catch (ParseException e) {
142               String msg = e.getMessage();
143               if (msg == null) {
144                   MessageOutput.printException(msg, e);
145               } else {
146                   String s;
147                   try {
148                       s = MessageOutput.format(msg);
149                   } catch (MissingResourceException mex) {
150                       s = e.toString();
151                   }
152                   MessageOutput.printDirectln(s);
153               }
154          }
155          return valStr;
156     }
157 
doGetThread(String idToken)158     private ThreadInfo doGetThread(String idToken) {
159         ThreadInfo threadInfo = ThreadInfo.getThreadInfo(idToken);
160         if (threadInfo == null) {
161             MessageOutput.println("is not a valid thread id", idToken);
162         }
163         return threadInfo;
164     }
165 
typedName(Method method)166     String typedName(Method method) {
167         StringBuilder sb = new StringBuilder();
168         sb.append(method.name());
169         sb.append("(");
170 
171         List<String> args = method.argumentTypeNames();
172         int lastParam = args.size() - 1;
173         // output param types except for the last
174         for (int ii = 0; ii < lastParam; ii++) {
175             sb.append(args.get(ii));
176             sb.append(", ");
177         }
178         if (lastParam >= 0) {
179             // output the last param
180             String lastStr = args.get(lastParam);
181             if (method.isVarArgs()) {
182                 // lastParam is an array.  Replace the [] with ...
183                 sb.append(lastStr.substring(0, lastStr.length() - 2));
184                 sb.append("...");
185             } else {
186                 sb.append(lastStr);
187             }
188         }
189         sb.append(")");
190         return sb.toString();
191     }
192 
commandConnectors(VirtualMachineManager vmm)193     void commandConnectors(VirtualMachineManager vmm) {
194         Collection<Connector> ccs = vmm.allConnectors();
195         if (ccs.isEmpty()) {
196             MessageOutput.println("Connectors available");
197         }
198         for (Connector cc : ccs) {
199             String transportName =
200                 cc.transport() == null ? "null" : cc.transport().name();
201             MessageOutput.println();
202             MessageOutput.println("Connector and Transport name",
203                                   new Object [] {cc.name(), transportName});
204             MessageOutput.println("Connector description", cc.description());
205 
206             for (Connector.Argument aa : cc.defaultArguments().values()) {
207                     MessageOutput.println();
208 
209                     boolean requiredArgument = aa.mustSpecify();
210                     if (aa.value() == null || aa.value() == "") {
211                         //no current value and no default.
212                         MessageOutput.println(requiredArgument ?
213                                               "Connector required argument nodefault" :
214                                               "Connector argument nodefault", aa.name());
215                     } else {
216                         MessageOutput.println(requiredArgument ?
217                                               "Connector required argument default" :
218                                               "Connector argument default",
219                                               new Object [] {aa.name(), aa.value()});
220                     }
221                     MessageOutput.println("Connector description", aa.description());
222 
223                 }
224             }
225 
226     }
227 
commandClasses()228     void commandClasses() {
229         StringBuilder classList = new StringBuilder();
230         for (ReferenceType refType : Env.vm().allClasses()) {
231             classList.append(refType.name());
232             classList.append("\n");
233         }
234         MessageOutput.print("** classes list **", classList.toString());
235     }
236 
commandClass(StringTokenizer t)237     void commandClass(StringTokenizer t) {
238 
239         if (!t.hasMoreTokens()) {
240             MessageOutput.println("No class specified.");
241             return;
242         }
243 
244         String idClass = t.nextToken();
245         boolean showAll = false;
246 
247         if (t.hasMoreTokens()) {
248             if (t.nextToken().toLowerCase().equals("all")) {
249                 showAll = true;
250             } else {
251                 MessageOutput.println("Invalid option on class command");
252                 return;
253             }
254         }
255         ReferenceType type = Env.getReferenceTypeFromToken(idClass);
256         if (type == null) {
257             MessageOutput.println("is not a valid id or class name", idClass);
258             return;
259         }
260         if (type instanceof ClassType) {
261             ClassType clazz = (ClassType)type;
262             MessageOutput.println("Class:", clazz.name());
263 
264             ClassType superclass = clazz.superclass();
265             while (superclass != null) {
266                 MessageOutput.println("extends:", superclass.name());
267                 superclass = showAll ? superclass.superclass() : null;
268             }
269 
270             List<InterfaceType> interfaces =
271                 showAll ? clazz.allInterfaces() : clazz.interfaces();
272             for (InterfaceType interfaze : interfaces) {
273                 MessageOutput.println("implements:", interfaze.name());
274             }
275 
276             for (ClassType sub : clazz.subclasses()) {
277                 MessageOutput.println("subclass:", sub.name());
278             }
279             for (ReferenceType nest : clazz.nestedTypes()) {
280                 MessageOutput.println("nested:", nest.name());
281             }
282         } else if (type instanceof InterfaceType) {
283             InterfaceType interfaze = (InterfaceType)type;
284             MessageOutput.println("Interface:", interfaze.name());
285             for (InterfaceType superinterface : interfaze.superinterfaces()) {
286                 MessageOutput.println("extends:", superinterface.name());
287             }
288             for (InterfaceType sub : interfaze.subinterfaces()) {
289                 MessageOutput.println("subinterface:", sub.name());
290             }
291             for (ClassType implementor : interfaze.implementors()) {
292                 MessageOutput.println("implementor:", implementor.name());
293             }
294             for (ReferenceType nest : interfaze.nestedTypes()) {
295                 MessageOutput.println("nested:", nest.name());
296             }
297         } else {  // array type
298             ArrayType array = (ArrayType)type;
299             MessageOutput.println("Array:", array.name());
300         }
301     }
302 
commandMethods(StringTokenizer t)303     void commandMethods(StringTokenizer t) {
304         if (!t.hasMoreTokens()) {
305             MessageOutput.println("No class specified.");
306             return;
307         }
308 
309         String idClass = t.nextToken();
310         ReferenceType cls = Env.getReferenceTypeFromToken(idClass);
311         if (cls != null) {
312             StringBuilder methodsList = new StringBuilder();
313             for (Method method : cls.allMethods()) {
314                 methodsList.append(method.declaringType().name());
315                 methodsList.append(" ");
316                 methodsList.append(typedName(method));
317                 methodsList.append('\n');
318             }
319             MessageOutput.print("** methods list **", methodsList.toString());
320         } else {
321             MessageOutput.println("is not a valid id or class name", idClass);
322         }
323     }
324 
commandFields(StringTokenizer t)325     void commandFields(StringTokenizer t) {
326         if (!t.hasMoreTokens()) {
327             MessageOutput.println("No class specified.");
328             return;
329         }
330 
331         String idClass = t.nextToken();
332         ReferenceType cls = Env.getReferenceTypeFromToken(idClass);
333         if (cls != null) {
334             List<Field> fields = cls.allFields();
335             List<Field> visible = cls.visibleFields();
336             StringBuilder fieldsList = new StringBuilder();
337             for (Field field : fields) {
338                 String s;
339                 if (!visible.contains(field)) {
340                     s = MessageOutput.format("list field typename and name hidden",
341                                              new Object [] {field.typeName(),
342                                                             field.name()});
343                 } else if (!field.declaringType().equals(cls)) {
344                     s = MessageOutput.format("list field typename and name inherited",
345                                              new Object [] {field.typeName(),
346                                                             field.name(),
347                                                             field.declaringType().name()});
348                 } else {
349                     s = MessageOutput.format("list field typename and name",
350                                              new Object [] {field.typeName(),
351                                                             field.name()});
352                 }
353                 fieldsList.append(s);
354             }
355             MessageOutput.print("** fields list **", fieldsList.toString());
356         } else {
357             MessageOutput.println("is not a valid id or class name", idClass);
358         }
359     }
360 
printThreadGroup(ThreadGroupReference tg)361     private void printThreadGroup(ThreadGroupReference tg) {
362         ThreadIterator threadIter = new ThreadIterator(tg);
363 
364         MessageOutput.println("Thread Group:", tg.name());
365         int maxIdLength = 0;
366         int maxNameLength = 0;
367         while (threadIter.hasNext()) {
368             ThreadReference thr = threadIter.next();
369             maxIdLength = Math.max(maxIdLength,
370                                    Env.description(thr).length());
371             maxNameLength = Math.max(maxNameLength,
372                                      thr.name().length());
373         }
374 
375         threadIter = new ThreadIterator(tg);
376         while (threadIter.hasNext()) {
377             ThreadReference thr = threadIter.next();
378             if (thr.threadGroup() == null) {
379                 continue;
380             }
381             // Note any thread group changes
382             if (!thr.threadGroup().equals(tg)) {
383                 tg = thr.threadGroup();
384                 MessageOutput.println("Thread Group:", tg.name());
385             }
386 
387             /*
388              * Do a bit of filling with whitespace to get thread ID
389              * and thread names to line up in the listing, and also
390              * allow for proper localization.  This also works for
391              * very long thread names, at the possible cost of lines
392              * being wrapped by the display device.
393              */
394             StringBuilder idBuffer = new StringBuilder(Env.description(thr));
395             for (int i = idBuffer.length(); i < maxIdLength; i++) {
396                 idBuffer.append(" ");
397             }
398             StringBuilder nameBuffer = new StringBuilder(thr.name());
399             for (int i = nameBuffer.length(); i < maxNameLength; i++) {
400                 nameBuffer.append(" ");
401             }
402 
403             /*
404              * Select the output format to use based on thread status
405              * and breakpoint.
406              */
407             String statusFormat;
408             switch (thr.status()) {
409             case ThreadReference.THREAD_STATUS_UNKNOWN:
410                 if (thr.isAtBreakpoint()) {
411                     statusFormat = "Thread description name unknownStatus BP";
412                 } else {
413                     statusFormat = "Thread description name unknownStatus";
414                 }
415                 break;
416             case ThreadReference.THREAD_STATUS_ZOMBIE:
417                 if (thr.isAtBreakpoint()) {
418                     statusFormat = "Thread description name zombieStatus BP";
419                 } else {
420                     statusFormat = "Thread description name zombieStatus";
421                 }
422                 break;
423             case ThreadReference.THREAD_STATUS_RUNNING:
424                 if (thr.isAtBreakpoint()) {
425                     statusFormat = "Thread description name runningStatus BP";
426                 } else {
427                     statusFormat = "Thread description name runningStatus";
428                 }
429                 break;
430             case ThreadReference.THREAD_STATUS_SLEEPING:
431                 if (thr.isAtBreakpoint()) {
432                     statusFormat = "Thread description name sleepingStatus BP";
433                 } else {
434                     statusFormat = "Thread description name sleepingStatus";
435                 }
436                 break;
437             case ThreadReference.THREAD_STATUS_MONITOR:
438                 if (thr.isAtBreakpoint()) {
439                     statusFormat = "Thread description name waitingStatus BP";
440                 } else {
441                     statusFormat = "Thread description name waitingStatus";
442                 }
443                 break;
444             case ThreadReference.THREAD_STATUS_WAIT:
445                 if (thr.isAtBreakpoint()) {
446                     statusFormat = "Thread description name condWaitstatus BP";
447                 } else {
448                     statusFormat = "Thread description name condWaitstatus";
449                 }
450                 break;
451             default:
452                 throw new InternalError(MessageOutput.format("Invalid thread status."));
453             }
454             MessageOutput.println(statusFormat,
455                                   new Object [] {idBuffer.toString(),
456                                                  nameBuffer.toString()});
457         }
458     }
459 
commandThreads(StringTokenizer t)460     void commandThreads(StringTokenizer t) {
461         if (!t.hasMoreTokens()) {
462             printThreadGroup(ThreadInfo.group());
463             return;
464         }
465         String name = t.nextToken();
466         ThreadGroupReference tg = ThreadGroupIterator.find(name);
467         if (tg == null) {
468             MessageOutput.println("is not a valid threadgroup name", name);
469         } else {
470             printThreadGroup(tg);
471         }
472     }
473 
commandThreadGroups()474     void commandThreadGroups() {
475         ThreadGroupIterator it = new ThreadGroupIterator();
476         int cnt = 0;
477         while (it.hasNext()) {
478             ThreadGroupReference tg = it.nextThreadGroup();
479             ++cnt;
480             MessageOutput.println("thread group number description name",
481                                   new Object [] { Integer.valueOf(cnt),
482                                                   Env.description(tg),
483                                                   tg.name()});
484         }
485     }
486 
commandThread(StringTokenizer t)487     void commandThread(StringTokenizer t) {
488         if (!t.hasMoreTokens()) {
489             MessageOutput.println("Thread number not specified.");
490             return;
491         }
492         ThreadInfo threadInfo = doGetThread(t.nextToken());
493         if (threadInfo != null) {
494             ThreadInfo.setCurrentThreadInfo(threadInfo);
495         }
496     }
497 
commandThreadGroup(StringTokenizer t)498     void commandThreadGroup(StringTokenizer t) {
499         if (!t.hasMoreTokens()) {
500             MessageOutput.println("Threadgroup name not specified.");
501             return;
502         }
503         String name = t.nextToken();
504         ThreadGroupReference tg = ThreadGroupIterator.find(name);
505         if (tg == null) {
506             MessageOutput.println("is not a valid threadgroup name", name);
507         } else {
508             ThreadInfo.setThreadGroup(tg);
509         }
510     }
511 
commandRun(StringTokenizer t)512     void commandRun(StringTokenizer t) {
513         /*
514          * The 'run' command makes little sense in a
515          * that doesn't support restarts or multiple VMs. However,
516          * this is an attempt to emulate the behavior of the old
517          * JDB as much as possible. For new users and implementations
518          * it is much more straightforward to launch immedidately
519          * with the -launch option.
520          */
521         VMConnection connection = Env.connection();
522         if (!connection.isLaunch()) {
523             if (!t.hasMoreTokens()) {
524                 commandCont();
525             } else {
526                 MessageOutput.println("run <args> command is valid only with launched VMs");
527             }
528             return;
529         }
530         if (connection.isOpen()) {
531             MessageOutput.println("VM already running. use cont to continue after events.");
532             return;
533         }
534 
535         /*
536          * Set the main class and any arguments. Note that this will work
537          * only with the standard launcher, "com.sun.jdi.CommandLineLauncher"
538          */
539         String args;
540         if (t.hasMoreTokens()) {
541             args = t.nextToken("");
542             boolean argsSet = connection.setConnectorArg("main", args);
543             if (!argsSet) {
544                 MessageOutput.println("Unable to set main class and arguments");
545                 return;
546             }
547         } else {
548             args = connection.connectorArg("main");
549             if (args.length() == 0) {
550                 MessageOutput.println("Main class and arguments must be specified");
551                 return;
552             }
553         }
554         MessageOutput.println("run", args);
555 
556         /*
557          * Launch the VM.
558          */
559         connection.open();
560 
561     }
562 
commandLoad(StringTokenizer t)563     void commandLoad(StringTokenizer t) {
564         MessageOutput.println("The load command is no longer supported.");
565     }
566 
allThreads(ThreadGroupReference group)567     private List<ThreadReference> allThreads(ThreadGroupReference group) {
568         List<ThreadReference> list = new ArrayList<ThreadReference>();
569         list.addAll(group.threads());
570         for (ThreadGroupReference child : group.threadGroups()) {
571             list.addAll(allThreads(child));
572         }
573         return list;
574     }
575 
commandSuspend(StringTokenizer t)576     void commandSuspend(StringTokenizer t) {
577         if (!t.hasMoreTokens()) {
578             Env.vm().suspend();
579             MessageOutput.println("All threads suspended.");
580         } else {
581             while (t.hasMoreTokens()) {
582                 ThreadInfo threadInfo = doGetThread(t.nextToken());
583                 if (threadInfo != null) {
584                     threadInfo.getThread().suspend();
585                 }
586             }
587         }
588     }
589 
commandResume(StringTokenizer t)590     void commandResume(StringTokenizer t) {
591          if (!t.hasMoreTokens()) {
592              ThreadInfo.invalidateAll();
593              Env.vm().resume();
594              MessageOutput.println("All threads resumed.");
595          } else {
596              while (t.hasMoreTokens()) {
597                 ThreadInfo threadInfo = doGetThread(t.nextToken());
598                 if (threadInfo != null) {
599                     threadInfo.invalidate();
600                     threadInfo.getThread().resume();
601                 }
602             }
603         }
604     }
605 
commandCont()606     void commandCont() {
607         if (ThreadInfo.getCurrentThreadInfo() == null) {
608             MessageOutput.println("Nothing suspended.");
609             return;
610         }
611         ThreadInfo.invalidateAll();
612         Env.vm().resume();
613     }
614 
clearPreviousStep(ThreadReference thread)615     void clearPreviousStep(ThreadReference thread) {
616         /*
617          * A previous step may not have completed on this thread;
618          * if so, it gets removed here.
619          */
620          EventRequestManager mgr = Env.vm().eventRequestManager();
621          for (StepRequest request : mgr.stepRequests()) {
622              if (request.thread().equals(thread)) {
623                  mgr.deleteEventRequest(request);
624                  break;
625              }
626          }
627     }
628     /* step
629      *
630      */
commandStep(StringTokenizer t)631     void commandStep(StringTokenizer t) {
632         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
633         if (threadInfo == null) {
634             MessageOutput.println("Nothing suspended.");
635             return;
636         }
637         int depth;
638         if (t.hasMoreTokens() &&
639                   t.nextToken().toLowerCase().equals("up")) {
640             depth = StepRequest.STEP_OUT;
641         } else {
642             depth = StepRequest.STEP_INTO;
643         }
644 
645         clearPreviousStep(threadInfo.getThread());
646         EventRequestManager reqMgr = Env.vm().eventRequestManager();
647         StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(),
648                                                        StepRequest.STEP_LINE, depth);
649         if (depth == StepRequest.STEP_INTO) {
650             Env.addExcludes(request);
651         }
652         // We want just the next step event and no others
653         request.addCountFilter(1);
654         request.enable();
655         ThreadInfo.invalidateAll();
656         Env.vm().resume();
657     }
658 
659     /* stepi
660      * step instruction.
661      */
commandStepi()662     void commandStepi() {
663         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
664         if (threadInfo == null) {
665             MessageOutput.println("Nothing suspended.");
666             return;
667         }
668         clearPreviousStep(threadInfo.getThread());
669         EventRequestManager reqMgr = Env.vm().eventRequestManager();
670         StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(),
671                                                        StepRequest.STEP_MIN,
672                                                        StepRequest.STEP_INTO);
673         Env.addExcludes(request);
674         // We want just the next step event and no others
675         request.addCountFilter(1);
676         request.enable();
677         ThreadInfo.invalidateAll();
678         Env.vm().resume();
679     }
680 
commandNext()681     void commandNext() {
682         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
683         if (threadInfo == null) {
684             MessageOutput.println("Nothing suspended.");
685             return;
686         }
687         clearPreviousStep(threadInfo.getThread());
688         EventRequestManager reqMgr = Env.vm().eventRequestManager();
689         StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(),
690                                                        StepRequest.STEP_LINE,
691                                                        StepRequest.STEP_OVER);
692         Env.addExcludes(request);
693         // We want just the next step event and no others
694         request.addCountFilter(1);
695         request.enable();
696         ThreadInfo.invalidateAll();
697         Env.vm().resume();
698     }
699 
doKill(ThreadReference thread, StringTokenizer t)700     void doKill(ThreadReference thread, StringTokenizer t) {
701         if (!t.hasMoreTokens()) {
702             MessageOutput.println("No exception object specified.");
703             return;
704         }
705         String expr = t.nextToken("");
706         Value val = evaluate(expr);
707         if ((val != null) && (val instanceof ObjectReference)) {
708             try {
709                 thread.stop((ObjectReference)val);
710                 MessageOutput.println("killed", thread.toString());
711             } catch (InvalidTypeException e) {
712                 MessageOutput.println("Invalid exception object");
713             }
714         } else {
715             MessageOutput.println("Expression must evaluate to an object");
716         }
717     }
718 
doKillThread(final ThreadReference threadToKill, final StringTokenizer tokenizer)719     void doKillThread(final ThreadReference threadToKill,
720                       final StringTokenizer tokenizer) {
721         new AsyncExecution() {
722                 @Override
723                 void action() {
724                     doKill(threadToKill, tokenizer);
725                 }
726             };
727     }
728 
commandKill(StringTokenizer t)729     void commandKill(StringTokenizer t) {
730         if (!t.hasMoreTokens()) {
731             MessageOutput.println("Usage: kill <thread id> <throwable>");
732             return;
733         }
734         ThreadInfo threadInfo = doGetThread(t.nextToken());
735         if (threadInfo != null) {
736             MessageOutput.println("killing thread:", threadInfo.getThread().name());
737             doKillThread(threadInfo.getThread(), t);
738             return;
739         }
740     }
741 
listCaughtExceptions()742     void listCaughtExceptions() {
743         boolean noExceptions = true;
744 
745         // Print a listing of the catch patterns currently in place
746         for (EventRequestSpec spec : Env.specList.eventRequestSpecs()) {
747             if (spec instanceof ExceptionSpec) {
748                 if (noExceptions) {
749                     noExceptions = false;
750                     MessageOutput.println("Exceptions caught:");
751                 }
752                 MessageOutput.println("tab", spec.toString());
753             }
754         }
755         if (noExceptions) {
756             MessageOutput.println("No exceptions caught.");
757         }
758     }
759 
parseExceptionSpec(StringTokenizer t)760     private EventRequestSpec parseExceptionSpec(StringTokenizer t) {
761         String notification = t.nextToken();
762         boolean notifyCaught = false;
763         boolean notifyUncaught = false;
764         EventRequestSpec spec = null;
765         String classPattern = null;
766 
767         if (notification.equals("uncaught")) {
768             notifyCaught = false;
769             notifyUncaught = true;
770         } else if (notification.equals("caught")) {
771             notifyCaught = true;
772             notifyUncaught = false;
773         } else if (notification.equals("all")) {
774             notifyCaught = true;
775             notifyUncaught = true;
776         } else {
777             /*
778              * Handle the same as "all" for backward
779              * compatibility with existing .jdbrc files.
780              *
781              * Insert an "all" and take the current token as the
782              * intended classPattern
783              *
784              */
785             notifyCaught = true;
786             notifyUncaught = true;
787             classPattern = notification;
788         }
789         if (classPattern == null && t.hasMoreTokens()) {
790             classPattern = t.nextToken();
791         }
792         if ((classPattern != null) && (notifyCaught || notifyUncaught)) {
793             try {
794                 spec = Env.specList.createExceptionCatch(classPattern,
795                                                          notifyCaught,
796                                                          notifyUncaught);
797             } catch (ClassNotFoundException exc) {
798                 MessageOutput.println("is not a valid class name", classPattern);
799             }
800         }
801         return spec;
802     }
803 
commandCatchException(StringTokenizer t)804     void commandCatchException(StringTokenizer t) {
805         if (!t.hasMoreTokens()) {
806             listCaughtExceptions();
807         } else {
808             EventRequestSpec spec = parseExceptionSpec(t);
809             if (spec != null) {
810                 resolveNow(spec);
811             } else {
812                 MessageOutput.println("Usage: catch exception");
813             }
814         }
815     }
816 
commandIgnoreException(StringTokenizer t)817     void commandIgnoreException(StringTokenizer t) {
818         if (!t.hasMoreTokens()) {
819             listCaughtExceptions();
820         } else {
821             EventRequestSpec spec = parseExceptionSpec(t);
822             if (Env.specList.delete(spec)) {
823                 MessageOutput.println("Removed:", spec.toString());
824             } else {
825                 if (spec != null) {
826                     MessageOutput.println("Not found:", spec.toString());
827                 }
828                 MessageOutput.println("Usage: ignore exception");
829             }
830         }
831     }
832 
commandUp(StringTokenizer t)833     void commandUp(StringTokenizer t) {
834         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
835         if (threadInfo == null) {
836             MessageOutput.println("Current thread not set.");
837             return;
838         }
839 
840         int nLevels = 1;
841         if (t.hasMoreTokens()) {
842             String idToken = t.nextToken();
843             int i;
844             try {
845                 NumberFormat nf = NumberFormat.getNumberInstance();
846                 nf.setParseIntegerOnly(true);
847                 Number n = nf.parse(idToken);
848                 i = n.intValue();
849             } catch (java.text.ParseException jtpe) {
850                 i = 0;
851             }
852             if (i <= 0) {
853                 MessageOutput.println("Usage: up [n frames]");
854                 return;
855             }
856             nLevels = i;
857         }
858 
859         try {
860             threadInfo.up(nLevels);
861         } catch (IncompatibleThreadStateException e) {
862             MessageOutput.println("Current thread isnt suspended.");
863         } catch (ArrayIndexOutOfBoundsException e) {
864             MessageOutput.println("End of stack.");
865         }
866     }
867 
commandDown(StringTokenizer t)868     void commandDown(StringTokenizer t) {
869         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
870         if (threadInfo == null) {
871             MessageOutput.println("Current thread not set.");
872             return;
873         }
874 
875         int nLevels = 1;
876         if (t.hasMoreTokens()) {
877             String idToken = t.nextToken();
878             int i;
879             try {
880                 NumberFormat nf = NumberFormat.getNumberInstance();
881                 nf.setParseIntegerOnly(true);
882                 Number n = nf.parse(idToken);
883                 i = n.intValue();
884             } catch (java.text.ParseException jtpe) {
885                 i = 0;
886             }
887             if (i <= 0) {
888                 MessageOutput.println("Usage: down [n frames]");
889                 return;
890             }
891             nLevels = i;
892         }
893 
894         try {
895             threadInfo.down(nLevels);
896         } catch (IncompatibleThreadStateException e) {
897             MessageOutput.println("Current thread isnt suspended.");
898         } catch (ArrayIndexOutOfBoundsException e) {
899             MessageOutput.println("End of stack.");
900         }
901     }
902 
dumpStack(ThreadInfo threadInfo, boolean showPC)903     private void dumpStack(ThreadInfo threadInfo, boolean showPC) {
904         List<StackFrame> stack = null;
905         try {
906             stack = threadInfo.getStack();
907         } catch (IncompatibleThreadStateException e) {
908             MessageOutput.println("Current thread isnt suspended.");
909             return;
910         }
911         if (stack == null) {
912             MessageOutput.println("Thread is not running (no stack).");
913         } else {
914             int nFrames = stack.size();
915             for (int i = threadInfo.getCurrentFrameIndex(); i < nFrames; i++) {
916                 StackFrame frame = stack.get(i);
917                 dumpFrame (i, showPC, frame);
918             }
919         }
920     }
921 
dumpFrame(int frameNumber, boolean showPC, StackFrame frame)922     private void dumpFrame (int frameNumber, boolean showPC, StackFrame frame) {
923         Location loc = frame.location();
924         long pc = -1;
925         if (showPC) {
926             pc = loc.codeIndex();
927         }
928         Method meth = loc.method();
929 
930         long lineNumber = loc.lineNumber();
931         String methodInfo = null;
932         if (meth.isNative()) {
933             methodInfo = MessageOutput.format("native method");
934         } else if (lineNumber != -1) {
935             try {
936                 methodInfo = loc.sourceName() +
937                     MessageOutput.format("line number",
938                                          new Object [] {Long.valueOf(lineNumber)});
939             } catch (AbsentInformationException e) {
940                 methodInfo = MessageOutput.format("unknown");
941             }
942         }
943         if (pc != -1) {
944             MessageOutput.println("stack frame dump with pc",
945                                   new Object [] {(frameNumber + 1),
946                                                  meth.declaringType().name(),
947                                                  meth.name(),
948                                                  methodInfo,
949                                                  Long.valueOf(pc)});
950         } else {
951             MessageOutput.println("stack frame dump",
952                                   new Object [] {(frameNumber + 1),
953                                                  meth.declaringType().name(),
954                                                  meth.name(),
955                                                  methodInfo});
956         }
957     }
958 
commandWhere(StringTokenizer t, boolean showPC)959     void commandWhere(StringTokenizer t, boolean showPC) {
960         if (!t.hasMoreTokens()) {
961             ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
962             if (threadInfo == null) {
963                 MessageOutput.println("No thread specified.");
964                 return;
965             }
966             dumpStack(threadInfo, showPC);
967         } else {
968             String token = t.nextToken();
969             if (token.toLowerCase().equals("all")) {
970                 for (ThreadInfo threadInfo : ThreadInfo.threads()) {
971                     MessageOutput.println("Thread:",
972                                           threadInfo.getThread().name());
973                     dumpStack(threadInfo, showPC);
974                 }
975             } else {
976                 ThreadInfo threadInfo = doGetThread(token);
977                 if (threadInfo != null) {
978                     ThreadInfo.setCurrentThreadInfo(threadInfo);
979                     dumpStack(threadInfo, showPC);
980                 }
981             }
982         }
983     }
984 
commandInterrupt(StringTokenizer t)985     void commandInterrupt(StringTokenizer t) {
986         if (!t.hasMoreTokens()) {
987             ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
988             if (threadInfo == null) {
989                 MessageOutput.println("No thread specified.");
990                 return;
991             }
992             threadInfo.getThread().interrupt();
993         } else {
994             ThreadInfo threadInfo = doGetThread(t.nextToken());
995             if (threadInfo != null) {
996                 threadInfo.getThread().interrupt();
997             }
998         }
999     }
1000 
commandMemory()1001     void commandMemory() {
1002         MessageOutput.println("The memory command is no longer supported.");
1003     }
1004 
commandGC()1005     void commandGC() {
1006         MessageOutput.println("The gc command is no longer necessary.");
1007     }
1008 
1009     /*
1010      * The next two methods are used by this class and by EventHandler
1011      * to print consistent locations and error messages.
1012      */
locationString(Location loc)1013     static String locationString(Location loc) {
1014         return MessageOutput.format("locationString",
1015                                     new Object [] {loc.declaringType().name(),
1016                                                    loc.method().name(),
1017                                                    Integer.valueOf(loc.lineNumber()),
1018                                                    Long.valueOf(loc.codeIndex())});
1019     }
1020 
listBreakpoints()1021     void listBreakpoints() {
1022         boolean noBreakpoints = true;
1023 
1024         // Print set breakpoints
1025         for (EventRequestSpec spec : Env.specList.eventRequestSpecs()) {
1026             if (spec instanceof BreakpointSpec) {
1027                 if (noBreakpoints) {
1028                     noBreakpoints = false;
1029                     MessageOutput.println("Breakpoints set:");
1030                 }
1031                 MessageOutput.println("tab", spec.toString());
1032             }
1033         }
1034         if (noBreakpoints) {
1035             MessageOutput.println("No breakpoints set.");
1036         }
1037     }
1038 
1039 
printBreakpointCommandUsage(String usageMessage)1040     private void printBreakpointCommandUsage(String usageMessage) {
1041         MessageOutput.println(usageMessage);
1042     }
1043 
parseBreakpointSpec(StringTokenizer t, String next_token, ThreadReference threadFilter, String usageMessage)1044     protected BreakpointSpec parseBreakpointSpec(StringTokenizer t, String next_token,
1045                                                  ThreadReference threadFilter,
1046                                                  String usageMessage) {
1047         BreakpointSpec breakpoint = null;
1048         try {
1049             String token = next_token;
1050 
1051             // We can't use hasMoreTokens here because it will cause any leading
1052             // paren to be lost.
1053             String rest;
1054             try {
1055                 rest = t.nextToken("").trim();
1056             } catch (NoSuchElementException e) {
1057                 rest = null;
1058             }
1059 
1060             if ((rest != null) && rest.startsWith(":")) {
1061                 t = new StringTokenizer(rest.substring(1));
1062                 String classId = token;
1063                 String lineToken = t.nextToken();
1064 
1065                 NumberFormat nf = NumberFormat.getNumberInstance();
1066                 nf.setParseIntegerOnly(true);
1067                 Number n;
1068                 try {
1069                     n = nf.parse(lineToken);
1070                 } catch (java.text.ParseException pe) {
1071                     MessageOutput.println("Invalid line number specified");
1072                     printBreakpointCommandUsage(usageMessage);
1073                     return null;
1074                 }
1075                 int lineNumber = n.intValue();
1076 
1077                 if (t.hasMoreTokens()) {
1078                     MessageOutput.println("Extra tokens after breakpoint location");
1079                     printBreakpointCommandUsage(usageMessage);
1080                     return null;
1081                 }
1082                 try {
1083                     breakpoint = Env.specList.createBreakpoint(classId,
1084                                                                lineNumber, threadFilter);
1085                 } catch (ClassNotFoundException exc) {
1086                     MessageOutput.println("is not a valid class name", classId);
1087                 }
1088             } else {
1089                 // Try stripping method from class.method token.
1090                 int idot = token.lastIndexOf('.');
1091                 if ( (idot <= 0) ||                     /* No dot or dot in first char */
1092                      (idot >= token.length() - 1) ) { /* dot in last char */
1093                     MessageOutput.println("Invalid <class>.<method_name> specification");
1094                     printBreakpointCommandUsage(usageMessage);
1095                     return null;
1096                 }
1097                 String methodName = token.substring(idot + 1);
1098                 String classId = token.substring(0, idot);
1099                 List<String> argumentList = null;
1100                 if (rest != null) {
1101                     if (!rest.startsWith("(") || !rest.endsWith(")")) {
1102                         MessageOutput.println("Invalid <method_name> specification:",
1103                                               methodName + rest);
1104                         printBreakpointCommandUsage(usageMessage);
1105                         return null;
1106                     }
1107                     // Trim the parens
1108                     rest = rest.substring(1, rest.length() - 1);
1109 
1110                     argumentList = new ArrayList<String>();
1111                     t = new StringTokenizer(rest, ",");
1112                     while (t.hasMoreTokens()) {
1113                         argumentList.add(t.nextToken());
1114                     }
1115                 }
1116                 try {
1117                     breakpoint = Env.specList.createBreakpoint(classId,
1118                                                                methodName,
1119                                                                threadFilter,
1120                                                                argumentList);
1121                 } catch (MalformedMemberNameException exc) {
1122                     MessageOutput.println("is not a valid method name", methodName);
1123                 } catch (ClassNotFoundException exc) {
1124                     MessageOutput.println("is not a valid class name", classId);
1125                 }
1126             }
1127         } catch (Exception e) {
1128             printBreakpointCommandUsage(usageMessage);
1129             return null;
1130         }
1131         return breakpoint;
1132     }
1133 
resolveNow(EventRequestSpec spec)1134     private void resolveNow(EventRequestSpec spec) {
1135         boolean success = Env.specList.addEagerlyResolve(spec);
1136         if (success && !spec.isResolved()) {
1137             MessageOutput.println("Deferring.", spec.toString());
1138         }
1139     }
1140 
commandDbgTrace(StringTokenizer t)1141     void commandDbgTrace(StringTokenizer t) {
1142         int traceFlags;
1143         if (t.hasMoreTokens()) {
1144             String flagStr = t.nextToken();
1145             try {
1146                 traceFlags = Integer.decode(flagStr).intValue();
1147             } catch (NumberFormatException nfe) {
1148                 MessageOutput.println("dbgtrace command value must be an integer:", flagStr);
1149                 return;
1150             }
1151         } else {
1152             traceFlags = VirtualMachine.TRACE_ALL;
1153         }
1154         Env.setTraceFlags(traceFlags);
1155     }
1156 
commandStop(StringTokenizer t)1157     void commandStop(StringTokenizer t) {
1158         byte suspendPolicy = EventRequest.SUSPEND_ALL;
1159         ThreadReference threadFilter = null;
1160 
1161         /*
1162          * Allowed syntax:
1163          *    stop [go|thread] [<thread_id>] <at|in> <location>
1164          * If no options are given, the current list of breakpoints is printed.
1165          * If "go" is specified, then immediately resume after stopping. No threads are suspended.
1166          * If "thread" is specified, then only suspend the thread we stop in.
1167          * If neither "go" nor "thread" are specified, then suspend all threads.
1168          * If an integer <thread_id> is specified, then only stop in the specified thread.
1169          * <location> can either be a line number or a method:
1170          *    - <class id>:<line>
1171          *    - <class id>.<method>[(argument_type,...)]
1172          */
1173 
1174         if (!t.hasMoreTokens()) {
1175             listBreakpoints();
1176             return;
1177         }
1178 
1179         String token = t.nextToken();
1180 
1181         /* Check for "go" or "thread" modifiers. */
1182         if (token.equals("go") && t.hasMoreTokens()) {
1183             suspendPolicy = EventRequest.SUSPEND_NONE;
1184             token = t.nextToken();
1185         } else if (token.equals("thread") && t.hasMoreTokens()) {
1186             suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
1187             token = t.nextToken();
1188         }
1189 
1190         /* Handle <thread_id> modifier. */
1191         if (!token.equals("at") && !token.equals("in")) {
1192             Long threadid;
1193             try {
1194                 threadid = Long.decode(token);
1195             } catch (NumberFormatException nfe) {
1196                 MessageOutput.println("Expected at, in, or an integer <thread_id>:", token);
1197                 printBreakpointCommandUsage("printstopcommandusage");
1198                 return;
1199             }
1200             try {
1201                 ThreadInfo threadInfo = ThreadInfo.getThreadInfo(token);
1202                 if (threadInfo == null) {
1203                     MessageOutput.println("Invalid <thread_id>:", token);
1204                     return;
1205                 }
1206                 threadFilter = threadInfo.getThread();
1207                 token = t.nextToken(BreakpointSpec.locationTokenDelimiter);
1208             } catch (VMNotConnectedException vmnce) {
1209                 MessageOutput.println("<thread_id> option not valid until the VM is started with the run command");
1210                 return;
1211             }
1212 
1213         }
1214 
1215         /* Make sure "at" or "in" comes next. */
1216         if (!token.equals("at") && !token.equals("in")) {
1217             MessageOutput.println("Missing at or in");
1218             printBreakpointCommandUsage("printstopcommandusage");
1219             return;
1220         }
1221 
1222         token = t.nextToken(BreakpointSpec.locationTokenDelimiter);
1223 
1224         BreakpointSpec spec = parseBreakpointSpec(t, token, threadFilter, "printstopcommandusage");
1225         if (spec != null) {
1226             spec.suspendPolicy = suspendPolicy;
1227             resolveNow(spec);
1228         }
1229     }
1230 
commandClear(StringTokenizer t)1231     void commandClear(StringTokenizer t) {
1232         if (!t.hasMoreTokens()) {
1233             listBreakpoints();
1234             return;
1235         }
1236 
1237         String token = t.nextToken(BreakpointSpec.locationTokenDelimiter);
1238         BreakpointSpec spec = parseBreakpointSpec(t, token, null, "printclearcommandusage");
1239         if (spec != null) {
1240             if (Env.specList.delete(spec)) {
1241                 MessageOutput.println("Removed:", spec.toString());
1242             } else {
1243                 MessageOutput.println("Not found:", spec.toString());
1244             }
1245         }
1246     }
1247 
parseWatchpointSpec(StringTokenizer t)1248     private List<WatchpointSpec> parseWatchpointSpec(StringTokenizer t) {
1249         List<WatchpointSpec> list = new ArrayList<WatchpointSpec>();
1250         boolean access = false;
1251         boolean modification = false;
1252         int suspendPolicy = EventRequest.SUSPEND_ALL;
1253 
1254         String fieldName = t.nextToken();
1255         if (fieldName.equals("go")) {
1256             suspendPolicy = EventRequest.SUSPEND_NONE;
1257             fieldName = t.nextToken();
1258         } else if (fieldName.equals("thread")) {
1259             suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
1260             fieldName = t.nextToken();
1261         }
1262         if (fieldName.equals("access")) {
1263             access = true;
1264             fieldName = t.nextToken();
1265         } else if (fieldName.equals("all")) {
1266             access = true;
1267             modification = true;
1268             fieldName = t.nextToken();
1269         } else {
1270             modification = true;
1271         }
1272         int dot = fieldName.lastIndexOf('.');
1273         if (dot < 0) {
1274             MessageOutput.println("Class containing field must be specified.");
1275             return list;
1276         }
1277         String className = fieldName.substring(0, dot);
1278         fieldName = fieldName.substring(dot+1);
1279 
1280         try {
1281             WatchpointSpec spec;
1282             if (access) {
1283                 spec = Env.specList.createAccessWatchpoint(className,
1284                                                            fieldName);
1285                 spec.suspendPolicy = suspendPolicy;
1286                 list.add(spec);
1287             }
1288             if (modification) {
1289                 spec = Env.specList.createModificationWatchpoint(className,
1290                                                                  fieldName);
1291                 spec.suspendPolicy = suspendPolicy;
1292                 list.add(spec);
1293             }
1294         } catch (MalformedMemberNameException exc) {
1295             MessageOutput.println("is not a valid field name", fieldName);
1296         } catch (ClassNotFoundException exc) {
1297             MessageOutput.println("is not a valid class name", className);
1298         }
1299         return list;
1300     }
1301 
commandWatch(StringTokenizer t)1302     void commandWatch(StringTokenizer t) {
1303         if (!t.hasMoreTokens()) {
1304             MessageOutput.println("Field to watch not specified");
1305             return;
1306         }
1307 
1308         for (WatchpointSpec spec : parseWatchpointSpec(t)) {
1309             resolveNow(spec);
1310         }
1311     }
1312 
commandUnwatch(StringTokenizer t)1313     void commandUnwatch(StringTokenizer t) {
1314         if (!t.hasMoreTokens()) {
1315             MessageOutput.println("Field to unwatch not specified");
1316             return;
1317         }
1318 
1319         for (WatchpointSpec spec : parseWatchpointSpec(t)) {
1320             if (Env.specList.delete(spec)) {
1321                 MessageOutput.println("Removed:", spec.toString());
1322             } else {
1323                 MessageOutput.println("Not found:", spec.toString());
1324             }
1325         }
1326     }
1327 
turnOnExitTrace(ThreadInfo threadInfo, int suspendPolicy)1328     void turnOnExitTrace(ThreadInfo threadInfo, int suspendPolicy) {
1329         EventRequestManager erm = Env.vm().eventRequestManager();
1330         MethodExitRequest exit = erm.createMethodExitRequest();
1331         if (threadInfo != null) {
1332             exit.addThreadFilter(threadInfo.getThread());
1333         }
1334         Env.addExcludes(exit);
1335         exit.setSuspendPolicy(suspendPolicy);
1336         exit.enable();
1337 
1338     }
1339 
1340     static String methodTraceCommand = null;
1341 
commandTrace(StringTokenizer t)1342     void commandTrace(StringTokenizer t) {
1343         String modif;
1344         int suspendPolicy = EventRequest.SUSPEND_ALL;
1345         ThreadInfo threadInfo = null;
1346         String goStr = " ";
1347 
1348         /*
1349          * trace [go] methods [thread]
1350          * trace [go] method exit | exits [thread]
1351          */
1352         if (t.hasMoreTokens()) {
1353             modif = t.nextToken();
1354             if (modif.equals("go")) {
1355                 suspendPolicy = EventRequest.SUSPEND_NONE;
1356                 goStr = " go ";
1357                 if (t.hasMoreTokens()) {
1358                     modif = t.nextToken();
1359                 }
1360             } else if (modif.equals("thread")) {
1361                 // this is undocumented as it doesn't work right.
1362                 suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
1363                 if (t.hasMoreTokens()) {
1364                     modif = t.nextToken();
1365                 }
1366             }
1367 
1368             if  (modif.equals("method")) {
1369                 String traceCmd = null;
1370 
1371                 if (t.hasMoreTokens()) {
1372                     String modif1 = t.nextToken();
1373                     if (modif1.equals("exits") || modif1.equals("exit")) {
1374                         if (t.hasMoreTokens()) {
1375                             threadInfo = doGetThread(t.nextToken());
1376                         }
1377                         if (modif1.equals("exit")) {
1378                             StackFrame frame;
1379                             try {
1380                                 frame = ThreadInfo.getCurrentThreadInfo().getCurrentFrame();
1381                             } catch (IncompatibleThreadStateException ee) {
1382                                 MessageOutput.println("Current thread isnt suspended.");
1383                                 return;
1384                             }
1385                             Env.setAtExitMethod(frame.location().method());
1386                             traceCmd = MessageOutput.format("trace" +
1387                                                     goStr + "method exit " +
1388                                                     "in effect for",
1389                                                     Env.atExitMethod().toString());
1390                         } else {
1391                             traceCmd = MessageOutput.format("trace" +
1392                                                    goStr + "method exits " +
1393                                                    "in effect");
1394                         }
1395                         commandUntrace(new StringTokenizer("methods"));
1396                         turnOnExitTrace(threadInfo, suspendPolicy);
1397                         methodTraceCommand = traceCmd;
1398                         return;
1399                     }
1400                 } else {
1401                    MessageOutput.println("Can only trace");
1402                    return;
1403                 }
1404             }
1405             if (modif.equals("methods")) {
1406                 // Turn on method entry trace
1407                 MethodEntryRequest entry;
1408                 EventRequestManager erm = Env.vm().eventRequestManager();
1409                 if (t.hasMoreTokens()) {
1410                     threadInfo = doGetThread(t.nextToken());
1411                 }
1412                 if (threadInfo != null) {
1413                     /*
1414                      * To keep things simple we want each 'trace' to cancel
1415                      * previous traces.  However in this case, we don't do that
1416                      * to preserve backward compatibility with pre JDK 6.0.
1417                      * IE, you can currently do
1418                      *   trace   methods 0x21
1419                      *   trace   methods 0x22
1420                      * and you will get xxx traced just on those two threads
1421                      * But this feature is kind of broken because if you then do
1422                      *   untrace  0x21
1423                      * it turns off both traces instead of just the one.
1424                      * Another bogosity is that if you do
1425                      *   trace methods
1426                      *   trace methods
1427                      * and you will get two traces.
1428                      */
1429 
1430                     entry = erm.createMethodEntryRequest();
1431                     entry.addThreadFilter(threadInfo.getThread());
1432                 } else {
1433                     commandUntrace(new StringTokenizer("methods"));
1434                     entry = erm.createMethodEntryRequest();
1435                 }
1436                 Env.addExcludes(entry);
1437                 entry.setSuspendPolicy(suspendPolicy);
1438                 entry.enable();
1439                 turnOnExitTrace(threadInfo, suspendPolicy);
1440                 methodTraceCommand = MessageOutput.format("trace" + goStr +
1441                                                           "methods in effect");
1442 
1443                 return;
1444             }
1445 
1446             MessageOutput.println("Can only trace");
1447             return;
1448         }
1449 
1450         // trace all by itself.
1451         if (methodTraceCommand != null) {
1452             MessageOutput.printDirectln(methodTraceCommand);
1453         }
1454 
1455         // More trace lines can be added here.
1456     }
1457 
commandUntrace(StringTokenizer t)1458     void commandUntrace(StringTokenizer t) {
1459         // untrace
1460         // untrace methods
1461 
1462         String modif = null;
1463         EventRequestManager erm = Env.vm().eventRequestManager();
1464         if (t.hasMoreTokens()) {
1465             modif = t.nextToken();
1466         }
1467         if (modif == null || modif.equals("methods")) {
1468             erm.deleteEventRequests(erm.methodEntryRequests());
1469             erm.deleteEventRequests(erm.methodExitRequests());
1470             Env.setAtExitMethod(null);
1471             methodTraceCommand = null;
1472         }
1473     }
1474 
commandList(StringTokenizer t)1475     void commandList(StringTokenizer t) {
1476         StackFrame frame = null;
1477         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1478         if (threadInfo == null) {
1479             MessageOutput.println("No thread specified.");
1480             return;
1481         }
1482         try {
1483             frame = threadInfo.getCurrentFrame();
1484         } catch (IncompatibleThreadStateException e) {
1485             MessageOutput.println("Current thread isnt suspended.");
1486             return;
1487         }
1488 
1489         if (frame == null) {
1490             MessageOutput.println("No frames on the current call stack");
1491             return;
1492         }
1493 
1494         Location loc = frame.location();
1495         if (loc.method().isNative()) {
1496             MessageOutput.println("Current method is native");
1497             return;
1498         }
1499 
1500         String sourceFileName = null;
1501         try {
1502             sourceFileName = loc.sourceName();
1503 
1504             ReferenceType refType = loc.declaringType();
1505             int lineno = loc.lineNumber();
1506 
1507             if (t.hasMoreTokens()) {
1508                 String id = t.nextToken();
1509 
1510                 // See if token is a line number.
1511                 try {
1512                     NumberFormat nf = NumberFormat.getNumberInstance();
1513                     nf.setParseIntegerOnly(true);
1514                     Number n = nf.parse(id);
1515                     lineno = n.intValue();
1516                 } catch (java.text.ParseException jtpe) {
1517                     // It isn't -- see if it's a method name.
1518                         List<Method> meths = refType.methodsByName(id);
1519                         if (meths == null || meths.size() == 0) {
1520                             MessageOutput.println("is not a valid line number or method name for",
1521                                                   new Object [] {id, refType.name()});
1522                             return;
1523                         } else if (meths.size() > 1) {
1524                             MessageOutput.println("is an ambiguous method name in",
1525                                                   new Object [] {id, refType.name()});
1526                             return;
1527                         }
1528                         loc = meths.get(0).location();
1529                         lineno = loc.lineNumber();
1530                 }
1531             }
1532             int startLine = Math.max(lineno - 4, 1);
1533             int endLine = startLine + 9;
1534             if (lineno < 0) {
1535                 MessageOutput.println("Line number information not available for");
1536             } else if (Env.sourceLine(loc, lineno) == null) {
1537                 MessageOutput.println("is an invalid line number for",
1538                                       new Object [] {Integer.valueOf(lineno),
1539                                                      refType.name()});
1540             } else {
1541                 for (int i = startLine; i <= endLine; i++) {
1542                     String sourceLine = Env.sourceLine(loc, i);
1543                     if (sourceLine == null) {
1544                         break;
1545                     }
1546                     if (i == lineno) {
1547                         MessageOutput.println("source line number current line and line",
1548                                               new Object [] {Integer.valueOf(i),
1549                                                              sourceLine});
1550                     } else {
1551                         MessageOutput.println("source line number and line",
1552                                               new Object [] {Integer.valueOf(i),
1553                                                              sourceLine});
1554                     }
1555                 }
1556             }
1557         } catch (AbsentInformationException e) {
1558             MessageOutput.println("No source information available for:", loc.toString());
1559         } catch(FileNotFoundException exc) {
1560             MessageOutput.println("Source file not found:", sourceFileName);
1561         } catch(IOException exc) {
1562             MessageOutput.println("I/O exception occurred:", exc.toString());
1563         }
1564     }
1565 
commandLines(StringTokenizer t)1566     void commandLines(StringTokenizer t) { // Undocumented command: useful for testing
1567         if (!t.hasMoreTokens()) {
1568             MessageOutput.println("Specify class and method");
1569         } else {
1570             String idClass = t.nextToken();
1571             String idMethod = t.hasMoreTokens() ? t.nextToken() : null;
1572             try {
1573                 ReferenceType refType = Env.getReferenceTypeFromToken(idClass);
1574                 if (refType != null) {
1575                     List<Location> lines = null;
1576                     if (idMethod == null) {
1577                         lines = refType.allLineLocations();
1578                     } else {
1579                         for (Method method : refType.allMethods()) {
1580                             if (method.name().equals(idMethod)) {
1581                                 lines = method.allLineLocations();
1582                             }
1583                         }
1584                         if (lines == null) {
1585                             MessageOutput.println("is not a valid method name", idMethod);
1586                         }
1587                     }
1588                     for (Location line : lines) {
1589                         MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
1590                     }
1591                 } else {
1592                     MessageOutput.println("is not a valid id or class name", idClass);
1593                 }
1594             } catch (AbsentInformationException e) {
1595                 MessageOutput.println("Line number information not available for", idClass);
1596             }
1597         }
1598     }
1599 
commandClasspath(StringTokenizer t)1600     void commandClasspath(StringTokenizer t) {
1601         if (Env.vm() instanceof PathSearchingVirtualMachine) {
1602             PathSearchingVirtualMachine vm = (PathSearchingVirtualMachine)Env.vm();
1603             MessageOutput.println("base directory:", vm.baseDirectory());
1604             MessageOutput.println("classpath:", vm.classPath().toString());
1605         } else {
1606             MessageOutput.println("The VM does not use paths");
1607         }
1608     }
1609 
1610     /* Get or set the source file path list. */
commandUse(StringTokenizer t)1611     void commandUse(StringTokenizer t) {
1612         if (!t.hasMoreTokens()) {
1613             MessageOutput.printDirectln(Env.getSourcePath());// Special case: use printDirectln()
1614         } else {
1615             /*
1616              * Take the remainder of the command line, minus
1617              * leading or trailing whitespace.  Embedded
1618              * whitespace is fine.
1619              */
1620             Env.setSourcePath(t.nextToken("").trim());
1621         }
1622     }
1623 
1624     /* Print a stack variable */
printVar(LocalVariable var, Value value)1625     private void printVar(LocalVariable var, Value value) {
1626         MessageOutput.println("expr is value",
1627                               new Object [] {var.name(),
1628                                              value == null ? "null" : value.toString()});
1629     }
1630 
1631     /* Print all local variables in current stack frame. */
commandLocals()1632     void commandLocals() {
1633         StackFrame frame;
1634         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1635         if (threadInfo == null) {
1636             MessageOutput.println("No default thread specified:");
1637             return;
1638         }
1639         try {
1640             frame = threadInfo.getCurrentFrame();
1641             if (frame == null) {
1642                 throw new AbsentInformationException();
1643             }
1644             List<LocalVariable> vars = frame.visibleVariables();
1645 
1646             if (vars.size() == 0) {
1647                 MessageOutput.println("No local variables");
1648                 return;
1649             }
1650             Map<LocalVariable, Value> values = frame.getValues(vars);
1651 
1652             MessageOutput.println("Method arguments:");
1653             for (LocalVariable var : vars) {
1654                 if (var.isArgument()) {
1655                     Value val = values.get(var);
1656                     printVar(var, val);
1657                 }
1658             }
1659             MessageOutput.println("Local variables:");
1660             for (LocalVariable var : vars) {
1661                 if (!var.isArgument()) {
1662                     Value val = values.get(var);
1663                     printVar(var, val);
1664                 }
1665             }
1666         } catch (AbsentInformationException aie) {
1667             MessageOutput.println("Local variable information not available.");
1668         } catch (IncompatibleThreadStateException exc) {
1669             MessageOutput.println("Current thread isnt suspended.");
1670         }
1671     }
1672 
dump(ObjectReference obj, ReferenceType refType, ReferenceType refTypeBase)1673     private void dump(ObjectReference obj, ReferenceType refType,
1674                       ReferenceType refTypeBase) {
1675         for (Field field : refType.fields()) {
1676             StringBuilder sb = new StringBuilder();
1677             sb.append("    ");
1678             if (!refType.equals(refTypeBase)) {
1679                 sb.append(refType.name());
1680                 sb.append(".");
1681             }
1682             sb.append(field.name());
1683             sb.append(MessageOutput.format("colon space"));
1684             sb.append(obj.getValue(field));
1685             MessageOutput.printDirectln(sb.toString()); // Special case: use printDirectln()
1686         }
1687         if (refType instanceof ClassType) {
1688             ClassType sup = ((ClassType)refType).superclass();
1689             if (sup != null) {
1690                 dump(obj, sup, refTypeBase);
1691             }
1692         } else if (refType instanceof InterfaceType) {
1693             for (InterfaceType sup : ((InterfaceType)refType).superinterfaces()) {
1694                 dump(obj, sup, refTypeBase);
1695             }
1696         } else {
1697             /* else refType is an instanceof ArrayType */
1698             if (obj instanceof ArrayReference) {
1699                 for (Iterator<Value> it = ((ArrayReference)obj).getValues().iterator();
1700                      it.hasNext(); ) {
1701                     MessageOutput.printDirect(it.next().toString());// Special case: use printDirect()
1702                     if (it.hasNext()) {
1703                         MessageOutput.printDirect(", ");// Special case: use printDirect()
1704                     }
1705                 }
1706                 MessageOutput.println();
1707             }
1708         }
1709     }
1710 
1711     /* Print a specified reference.
1712      */
doPrint(StringTokenizer t, boolean dumpObject)1713     void doPrint(StringTokenizer t, boolean dumpObject) {
1714         if (!t.hasMoreTokens()) {
1715             MessageOutput.println("No objects specified.");
1716             return;
1717         }
1718 
1719         while (t.hasMoreTokens()) {
1720             String expr = t.nextToken("");
1721             Value val = evaluate(expr);
1722             if (val == null) {
1723                 MessageOutput.println("expr is null", expr.toString());
1724             } else if (dumpObject && (val instanceof ObjectReference) &&
1725                        !(val instanceof StringReference)) {
1726                 ObjectReference obj = (ObjectReference)val;
1727                 ReferenceType refType = obj.referenceType();
1728                 MessageOutput.println("expr is value",
1729                                       new Object [] {expr.toString(),
1730                                                      MessageOutput.format("grouping begin character")});
1731                 dump(obj, refType, refType);
1732                 MessageOutput.println("grouping end character");
1733             } else {
1734                   String strVal = getStringValue();
1735                   if (strVal != null) {
1736                      MessageOutput.println("expr is value", new Object [] {expr.toString(),
1737                                                                       strVal});
1738                    }
1739             }
1740         }
1741     }
1742 
commandPrint(final StringTokenizer t, final boolean dumpObject)1743     void commandPrint(final StringTokenizer t, final boolean dumpObject) {
1744         new AsyncExecution() {
1745                 @Override
1746                 void action() {
1747                     doPrint(t, dumpObject);
1748                 }
1749             };
1750     }
1751 
commandSet(final StringTokenizer t)1752     void commandSet(final StringTokenizer t) {
1753         String all = t.nextToken("");
1754 
1755         /*
1756          * Bare bones error checking.
1757          */
1758         if (all.indexOf('=') == -1) {
1759             MessageOutput.println("Invalid assignment syntax");
1760             MessageOutput.printPrompt();
1761             return;
1762         }
1763 
1764         /*
1765          * The set command is really just syntactic sugar. Pass it on to the
1766          * print command.
1767          */
1768         commandPrint(new StringTokenizer(all), false);
1769     }
1770 
doLock(StringTokenizer t)1771     void doLock(StringTokenizer t) {
1772         if (!t.hasMoreTokens()) {
1773             MessageOutput.println("No object specified.");
1774             return;
1775         }
1776 
1777         String expr = t.nextToken("");
1778         Value val = evaluate(expr);
1779 
1780         try {
1781             if ((val != null) && (val instanceof ObjectReference)) {
1782                 ObjectReference object = (ObjectReference)val;
1783                 String strVal = getStringValue();
1784                 if (strVal != null) {
1785                     MessageOutput.println("Monitor information for expr",
1786                                       new Object [] {expr.trim(),
1787                                                      strVal});
1788                 }
1789                 ThreadReference owner = object.owningThread();
1790                 if (owner == null) {
1791                     MessageOutput.println("Not owned");
1792                 } else {
1793                     MessageOutput.println("Owned by:",
1794                                           new Object [] {owner.name(),
1795                                                          Integer.valueOf(object.entryCount())});
1796                 }
1797                 List<ThreadReference> waiters = object.waitingThreads();
1798                 if (waiters.size() == 0) {
1799                     MessageOutput.println("No waiters");
1800                 } else {
1801                     for (ThreadReference waiter : waiters) {
1802                         MessageOutput.println("Waiting thread:", waiter.name());
1803                     }
1804                 }
1805             } else {
1806                 MessageOutput.println("Expression must evaluate to an object");
1807             }
1808         } catch (IncompatibleThreadStateException e) {
1809             MessageOutput.println("Threads must be suspended");
1810         }
1811     }
1812 
commandLock(final StringTokenizer t)1813     void commandLock(final StringTokenizer t) {
1814         new AsyncExecution() {
1815                 @Override
1816                 void action() {
1817                     doLock(t);
1818                 }
1819             };
1820     }
1821 
printThreadLockInfo(ThreadInfo threadInfo)1822     private void printThreadLockInfo(ThreadInfo threadInfo) {
1823         ThreadReference thread = threadInfo.getThread();
1824         try {
1825             MessageOutput.println("Monitor information for thread", thread.name());
1826             List<ObjectReference> owned = thread.ownedMonitors();
1827             if (owned.size() == 0) {
1828                 MessageOutput.println("No monitors owned");
1829             } else {
1830                 for (ObjectReference monitor : owned) {
1831                     MessageOutput.println("Owned monitor:", monitor.toString());
1832                 }
1833             }
1834             ObjectReference waiting = thread.currentContendedMonitor();
1835             if (waiting == null) {
1836                 MessageOutput.println("Not waiting for a monitor");
1837             } else {
1838                 MessageOutput.println("Waiting for monitor:", waiting.toString());
1839             }
1840         } catch (IncompatibleThreadStateException e) {
1841             MessageOutput.println("Threads must be suspended");
1842         }
1843     }
1844 
commandThreadlocks(final StringTokenizer t)1845     void commandThreadlocks(final StringTokenizer t) {
1846         if (!t.hasMoreTokens()) {
1847             ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1848             if (threadInfo == null) {
1849                 MessageOutput.println("Current thread not set.");
1850             } else {
1851                 printThreadLockInfo(threadInfo);
1852             }
1853             return;
1854         }
1855         String token = t.nextToken();
1856         if (token.toLowerCase().equals("all")) {
1857             for (ThreadInfo threadInfo : ThreadInfo.threads()) {
1858                 printThreadLockInfo(threadInfo);
1859             }
1860         } else {
1861             ThreadInfo threadInfo = doGetThread(token);
1862             if (threadInfo != null) {
1863                 ThreadInfo.setCurrentThreadInfo(threadInfo);
1864                 printThreadLockInfo(threadInfo);
1865             }
1866         }
1867     }
1868 
doDisableGC(StringTokenizer t)1869     void doDisableGC(StringTokenizer t) {
1870         if (!t.hasMoreTokens()) {
1871             MessageOutput.println("No object specified.");
1872             return;
1873         }
1874 
1875         String expr = t.nextToken("");
1876         Value val = evaluate(expr);
1877         if ((val != null) && (val instanceof ObjectReference)) {
1878             ObjectReference object = (ObjectReference)val;
1879             object.disableCollection();
1880             String strVal = getStringValue();
1881             if (strVal != null) {
1882                  MessageOutput.println("GC Disabled for", strVal);
1883             }
1884         } else {
1885             MessageOutput.println("Expression must evaluate to an object");
1886         }
1887     }
1888 
commandDisableGC(final StringTokenizer t)1889     void commandDisableGC(final StringTokenizer t) {
1890         new AsyncExecution() {
1891                 @Override
1892                 void action() {
1893                     doDisableGC(t);
1894                 }
1895             };
1896     }
1897 
doEnableGC(StringTokenizer t)1898     void doEnableGC(StringTokenizer t) {
1899         if (!t.hasMoreTokens()) {
1900             MessageOutput.println("No object specified.");
1901             return;
1902         }
1903 
1904         String expr = t.nextToken("");
1905         Value val = evaluate(expr);
1906         if ((val != null) && (val instanceof ObjectReference)) {
1907             ObjectReference object = (ObjectReference)val;
1908             object.enableCollection();
1909             String strVal = getStringValue();
1910             if (strVal != null) {
1911                  MessageOutput.println("GC Enabled for", strVal);
1912             }
1913         } else {
1914             MessageOutput.println("Expression must evaluate to an object");
1915         }
1916     }
1917 
commandEnableGC(final StringTokenizer t)1918     void commandEnableGC(final StringTokenizer t) {
1919         new AsyncExecution() {
1920                 @Override
1921                 void action() {
1922                     doEnableGC(t);
1923                 }
1924             };
1925     }
1926 
doSave(StringTokenizer t)1927     void doSave(StringTokenizer t) {// Undocumented command: useful for testing.
1928         if (!t.hasMoreTokens()) {
1929             MessageOutput.println("No save index specified.");
1930             return;
1931         }
1932 
1933         String key = t.nextToken();
1934 
1935         if (!t.hasMoreTokens()) {
1936             MessageOutput.println("No expression specified.");
1937             return;
1938         }
1939         String expr = t.nextToken("");
1940         Value val = evaluate(expr);
1941         if (val != null) {
1942             Env.setSavedValue(key, val);
1943             String strVal = getStringValue();
1944             if (strVal != null) {
1945                  MessageOutput.println("saved", strVal);
1946             }
1947         } else {
1948             MessageOutput.println("Expression cannot be void");
1949         }
1950     }
1951 
commandSave(final StringTokenizer t)1952     void commandSave(final StringTokenizer t) { // Undocumented command: useful for testing.
1953         if (!t.hasMoreTokens()) {
1954             Set<String> keys = Env.getSaveKeys();
1955             if (keys.isEmpty()) {
1956                 MessageOutput.println("No saved values");
1957                 return;
1958             }
1959             for (String key : keys) {
1960                 Value value = Env.getSavedValue(key);
1961                 if ((value instanceof ObjectReference) &&
1962                     ((ObjectReference)value).isCollected()) {
1963                     MessageOutput.println("expr is value <collected>",
1964                                           new Object [] {key, value.toString()});
1965                 } else {
1966                     if (value == null){
1967                         MessageOutput.println("expr is null", key);
1968                     } else {
1969                         MessageOutput.println("expr is value",
1970                                               new Object [] {key, value.toString()});
1971                     }
1972                 }
1973             }
1974         } else {
1975             new AsyncExecution() {
1976                     @Override
1977                     void action() {
1978                         doSave(t);
1979                     }
1980                 };
1981         }
1982 
1983     }
1984 
commandBytecodes(final StringTokenizer t)1985    void commandBytecodes(final StringTokenizer t) { // Undocumented command: useful for testing.
1986         if (!t.hasMoreTokens()) {
1987             MessageOutput.println("No class specified.");
1988             return;
1989         }
1990         String className = t.nextToken();
1991 
1992         if (!t.hasMoreTokens()) {
1993             MessageOutput.println("No method specified.");
1994             return;
1995         }
1996         // Overloading is not handled here.
1997         String methodName = t.nextToken();
1998 
1999         List<ReferenceType> classes = Env.vm().classesByName(className);
2000         // TO DO: handle multiple classes found
2001         if (classes.size() == 0) {
2002             if (className.indexOf('.') < 0) {
2003                 MessageOutput.println("not found (try the full name)", className);
2004             } else {
2005                 MessageOutput.println("not found", className);
2006             }
2007             return;
2008         }
2009 
2010         ReferenceType rt = classes.get(0);
2011         if (!(rt instanceof ClassType)) {
2012             MessageOutput.println("not a class", className);
2013             return;
2014         }
2015 
2016         byte[] bytecodes = null;
2017         for (Method method : rt.methodsByName(methodName)) {
2018             if (!method.isAbstract()) {
2019                 bytecodes = method.bytecodes();
2020                 break;
2021             }
2022         }
2023 
2024         StringBuilder line = new StringBuilder(80);
2025         line.append("0000: ");
2026         for (int i = 0; i < bytecodes.length; i++) {
2027             if ((i > 0) && (i % 16 == 0)) {
2028                 MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
2029                 line.setLength(0);
2030                 line.append(String.valueOf(i));
2031                 line.append(": ");
2032                 int len = line.length();
2033                 for (int j = 0; j < 6 - len; j++) {
2034                     line.insert(0, '0');
2035                 }
2036             }
2037             int val = 0xff & bytecodes[i];
2038             String str = Integer.toHexString(val);
2039             if (str.length() == 1) {
2040                 line.append('0');
2041             }
2042             line.append(str);
2043             line.append(' ');
2044         }
2045         if (line.length() > 6) {
2046             MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
2047         }
2048     }
2049 
commandExclude(StringTokenizer t)2050     void commandExclude(StringTokenizer t) {
2051         if (!t.hasMoreTokens()) {
2052             MessageOutput.printDirectln(Env.excludesString());// Special case: use printDirectln()
2053         } else {
2054             String rest = t.nextToken("");
2055             if (rest.equals("none")) {
2056                 rest = "";
2057             }
2058             Env.setExcludes(rest);
2059         }
2060     }
2061 
commandRedefine(StringTokenizer t)2062     void commandRedefine(StringTokenizer t) {
2063         if (!t.hasMoreTokens()) {
2064             MessageOutput.println("Specify classes to redefine");
2065         } else {
2066             String className = t.nextToken();
2067             List<ReferenceType> classes = Env.vm().classesByName(className);
2068             if (classes.size() == 0) {
2069                 MessageOutput.println("No class named", className);
2070                 return;
2071             }
2072             if (classes.size() > 1) {
2073                 MessageOutput.println("More than one class named", className);
2074                 return;
2075             }
2076             Env.setSourcePath(Env.getSourcePath());
2077             ReferenceType refType = classes.get(0);
2078             if (!t.hasMoreTokens()) {
2079                 MessageOutput.println("Specify file name for class", className);
2080                 return;
2081             }
2082             String fileName = t.nextToken();
2083             File phyl = new File(fileName);
2084             byte[] bytes = new byte[(int)phyl.length()];
2085             try {
2086                 InputStream in = new FileInputStream(phyl);
2087                 in.read(bytes);
2088                 in.close();
2089             } catch (Exception exc) {
2090                 MessageOutput.println("Error reading file",
2091                              new Object [] {fileName, exc.toString()});
2092                 return;
2093             }
2094             Map<ReferenceType, byte[]> map
2095                 = new HashMap<ReferenceType, byte[]>();
2096             map.put(refType, bytes);
2097             try {
2098                 Env.vm().redefineClasses(map);
2099             } catch (Throwable exc) {
2100                 MessageOutput.println("Error redefining class to file",
2101                              new Object [] {className,
2102                                             fileName,
2103                                             exc});
2104             }
2105         }
2106     }
2107 
commandPopFrames(StringTokenizer t, boolean reenter)2108     void commandPopFrames(StringTokenizer t, boolean reenter) {
2109         ThreadInfo threadInfo;
2110 
2111         if (t.hasMoreTokens()) {
2112             String token = t.nextToken();
2113             threadInfo = doGetThread(token);
2114             if (threadInfo == null) {
2115                 return;
2116             }
2117         } else {
2118             threadInfo = ThreadInfo.getCurrentThreadInfo();
2119             if (threadInfo == null) {
2120                 MessageOutput.println("No thread specified.");
2121                 return;
2122             }
2123         }
2124 
2125         try {
2126             StackFrame frame = threadInfo.getCurrentFrame();
2127             threadInfo.getThread().popFrames(frame);
2128             threadInfo = ThreadInfo.getCurrentThreadInfo();
2129             ThreadInfo.setCurrentThreadInfo(threadInfo);
2130             if (reenter) {
2131                 commandStepi();
2132             }
2133         } catch (Throwable exc) {
2134             MessageOutput.println("Error popping frame", exc.toString());
2135         }
2136     }
2137 
commandExtension(StringTokenizer t)2138     void commandExtension(StringTokenizer t) {
2139         if (!t.hasMoreTokens()) {
2140             MessageOutput.println("No class specified.");
2141             return;
2142         }
2143 
2144         String idClass = t.nextToken();
2145         ReferenceType cls = Env.getReferenceTypeFromToken(idClass);
2146         String extension = null;
2147         if (cls != null) {
2148             try {
2149                 extension = cls.sourceDebugExtension();
2150                 MessageOutput.println("sourcedebugextension", extension);
2151             } catch (AbsentInformationException e) {
2152                 MessageOutput.println("No sourcedebugextension specified");
2153             }
2154         } else {
2155             MessageOutput.println("is not a valid id or class name", idClass);
2156         }
2157     }
2158 
commandVersion(String debuggerName, VirtualMachineManager vmm)2159     void commandVersion(String debuggerName,
2160                         VirtualMachineManager vmm) {
2161         MessageOutput.println("minus version",
2162                               new Object [] { debuggerName,
2163                                               vmm.majorInterfaceVersion(),
2164                                               vmm.minorInterfaceVersion(),
2165                                                   System.getProperty("java.version")});
2166         if (Env.connection() != null) {
2167             try {
2168                 MessageOutput.printDirectln(Env.vm().description());// Special case: use printDirectln()
2169             } catch (VMNotConnectedException e) {
2170                 MessageOutput.println("No VM connected");
2171             }
2172         }
2173     }
2174 }
2175