1/*
2 * Copyright (c) 1998, 2011, 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
35package com.sun.tools.example.debug.tty;
36
37import com.sun.jdi.*;
38import com.sun.jdi.connect.Connector;
39import com.sun.jdi.request.*;
40import com.sun.tools.example.debug.expr.ExpressionParser;
41import com.sun.tools.example.debug.expr.ParseException;
42
43import java.text.*;
44import java.util.*;
45import java.io.*;
46
47class Commands {
48
49    abstract class AsyncExecution {
50        abstract void action();
51
52        AsyncExecution() {
53            execute();
54        }
55
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
98    Commands() {
99    }
100
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
563    void commandLoad(StringTokenizer t) {
564        MessageOutput.println("The load command is no longer supported.");
565    }
566
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
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
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
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
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     */
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     */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1001    void commandMemory() {
1002        MessageOutput.println("The memory command is no longer supported.");
1003    }
1004
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     */
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
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
1040    private void printBreakpointCommandUsage(String atForm, String inForm) {
1041        MessageOutput.println("printbreakpointcommandusage",
1042                              new Object [] {atForm, inForm});
1043    }
1044
1045    protected BreakpointSpec parseBreakpointSpec(StringTokenizer t,
1046                                             String atForm, String inForm) {
1047        BreakpointSpec breakpoint = null;
1048        try {
1049            String token = t.nextToken(":( \t\n\r");
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 = nf.parse(lineToken);
1068                int lineNumber = n.intValue();
1069
1070                if (t.hasMoreTokens()) {
1071                    printBreakpointCommandUsage(atForm, inForm);
1072                    return null;
1073                }
1074                try {
1075                    breakpoint = Env.specList.createBreakpoint(classId,
1076                                                               lineNumber);
1077                } catch (ClassNotFoundException exc) {
1078                    MessageOutput.println("is not a valid class name", classId);
1079                }
1080            } else {
1081                // Try stripping method from class.method token.
1082                int idot = token.lastIndexOf('.');
1083                if ( (idot <= 0) ||                     /* No dot or dot in first char */
1084                     (idot >= token.length() - 1) ) { /* dot in last char */
1085                    printBreakpointCommandUsage(atForm, inForm);
1086                    return null;
1087                }
1088                String methodName = token.substring(idot + 1);
1089                String classId = token.substring(0, idot);
1090                List<String> argumentList = null;
1091                if (rest != null) {
1092                    if (!rest.startsWith("(") || !rest.endsWith(")")) {
1093                        MessageOutput.println("Invalid method specification:",
1094                                              methodName + rest);
1095                        printBreakpointCommandUsage(atForm, inForm);
1096                        return null;
1097                    }
1098                    // Trim the parens
1099                    rest = rest.substring(1, rest.length() - 1);
1100
1101                    argumentList = new ArrayList<String>();
1102                    t = new StringTokenizer(rest, ",");
1103                    while (t.hasMoreTokens()) {
1104                        argumentList.add(t.nextToken());
1105                    }
1106                }
1107                try {
1108                    breakpoint = Env.specList.createBreakpoint(classId,
1109                                                               methodName,
1110                                                               argumentList);
1111                } catch (MalformedMemberNameException exc) {
1112                    MessageOutput.println("is not a valid method name", methodName);
1113                } catch (ClassNotFoundException exc) {
1114                    MessageOutput.println("is not a valid class name", classId);
1115                }
1116            }
1117        } catch (Exception e) {
1118            printBreakpointCommandUsage(atForm, inForm);
1119            return null;
1120        }
1121        return breakpoint;
1122    }
1123
1124    private void resolveNow(EventRequestSpec spec) {
1125        boolean success = Env.specList.addEagerlyResolve(spec);
1126        if (success && !spec.isResolved()) {
1127            MessageOutput.println("Deferring.", spec.toString());
1128        }
1129    }
1130
1131    void commandStop(StringTokenizer t) {
1132        String atIn;
1133        byte suspendPolicy = EventRequest.SUSPEND_ALL;
1134
1135        if (t.hasMoreTokens()) {
1136            atIn = t.nextToken();
1137            if (atIn.equals("go") && t.hasMoreTokens()) {
1138                suspendPolicy = EventRequest.SUSPEND_NONE;
1139                atIn = t.nextToken();
1140            } else if (atIn.equals("thread") && t.hasMoreTokens()) {
1141                suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
1142                atIn = t.nextToken();
1143            }
1144        } else {
1145            listBreakpoints();
1146            return;
1147        }
1148
1149        BreakpointSpec spec = parseBreakpointSpec(t, "stop at", "stop in");
1150        if (spec != null) {
1151            // Enforcement of "at" vs. "in". The distinction is really
1152            // unnecessary and we should consider not checking for this
1153            // (and making "at" and "in" optional).
1154            if (atIn.equals("at") && spec.isMethodBreakpoint()) {
1155                MessageOutput.println("Use stop at to set a breakpoint at a line number");
1156                printBreakpointCommandUsage("stop at", "stop in");
1157                return;
1158            }
1159            spec.suspendPolicy = suspendPolicy;
1160            resolveNow(spec);
1161        }
1162    }
1163
1164    void commandClear(StringTokenizer t) {
1165        if (!t.hasMoreTokens()) {
1166            listBreakpoints();
1167            return;
1168        }
1169
1170        BreakpointSpec spec = parseBreakpointSpec(t, "clear", "clear");
1171        if (spec != null) {
1172            if (Env.specList.delete(spec)) {
1173                MessageOutput.println("Removed:", spec.toString());
1174            } else {
1175                MessageOutput.println("Not found:", spec.toString());
1176            }
1177        }
1178    }
1179
1180    private List<WatchpointSpec> parseWatchpointSpec(StringTokenizer t) {
1181        List<WatchpointSpec> list = new ArrayList<WatchpointSpec>();
1182        boolean access = false;
1183        boolean modification = false;
1184        int suspendPolicy = EventRequest.SUSPEND_ALL;
1185
1186        String fieldName = t.nextToken();
1187        if (fieldName.equals("go")) {
1188            suspendPolicy = EventRequest.SUSPEND_NONE;
1189            fieldName = t.nextToken();
1190        } else if (fieldName.equals("thread")) {
1191            suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
1192            fieldName = t.nextToken();
1193        }
1194        if (fieldName.equals("access")) {
1195            access = true;
1196            fieldName = t.nextToken();
1197        } else if (fieldName.equals("all")) {
1198            access = true;
1199            modification = true;
1200            fieldName = t.nextToken();
1201        } else {
1202            modification = true;
1203        }
1204        int dot = fieldName.lastIndexOf('.');
1205        if (dot < 0) {
1206            MessageOutput.println("Class containing field must be specified.");
1207            return list;
1208        }
1209        String className = fieldName.substring(0, dot);
1210        fieldName = fieldName.substring(dot+1);
1211
1212        try {
1213            WatchpointSpec spec;
1214            if (access) {
1215                spec = Env.specList.createAccessWatchpoint(className,
1216                                                           fieldName);
1217                spec.suspendPolicy = suspendPolicy;
1218                list.add(spec);
1219            }
1220            if (modification) {
1221                spec = Env.specList.createModificationWatchpoint(className,
1222                                                                 fieldName);
1223                spec.suspendPolicy = suspendPolicy;
1224                list.add(spec);
1225            }
1226        } catch (MalformedMemberNameException exc) {
1227            MessageOutput.println("is not a valid field name", fieldName);
1228        } catch (ClassNotFoundException exc) {
1229            MessageOutput.println("is not a valid class name", className);
1230        }
1231        return list;
1232    }
1233
1234    void commandWatch(StringTokenizer t) {
1235        if (!t.hasMoreTokens()) {
1236            MessageOutput.println("Field to watch not specified");
1237            return;
1238        }
1239
1240        for (WatchpointSpec spec : parseWatchpointSpec(t)) {
1241            resolveNow(spec);
1242        }
1243    }
1244
1245    void commandUnwatch(StringTokenizer t) {
1246        if (!t.hasMoreTokens()) {
1247            MessageOutput.println("Field to unwatch not specified");
1248            return;
1249        }
1250
1251        for (WatchpointSpec spec : parseWatchpointSpec(t)) {
1252            if (Env.specList.delete(spec)) {
1253                MessageOutput.println("Removed:", spec.toString());
1254            } else {
1255                MessageOutput.println("Not found:", spec.toString());
1256            }
1257        }
1258    }
1259
1260    void turnOnExitTrace(ThreadInfo threadInfo, int suspendPolicy) {
1261        EventRequestManager erm = Env.vm().eventRequestManager();
1262        MethodExitRequest exit = erm.createMethodExitRequest();
1263        if (threadInfo != null) {
1264            exit.addThreadFilter(threadInfo.getThread());
1265        }
1266        Env.addExcludes(exit);
1267        exit.setSuspendPolicy(suspendPolicy);
1268        exit.enable();
1269
1270    }
1271
1272    static String methodTraceCommand = null;
1273
1274    void commandTrace(StringTokenizer t) {
1275        String modif;
1276        int suspendPolicy = EventRequest.SUSPEND_ALL;
1277        ThreadInfo threadInfo = null;
1278        String goStr = " ";
1279
1280        /*
1281         * trace [go] methods [thread]
1282         * trace [go] method exit | exits [thread]
1283         */
1284        if (t.hasMoreTokens()) {
1285            modif = t.nextToken();
1286            if (modif.equals("go")) {
1287                suspendPolicy = EventRequest.SUSPEND_NONE;
1288                goStr = " go ";
1289                if (t.hasMoreTokens()) {
1290                    modif = t.nextToken();
1291                }
1292            } else if (modif.equals("thread")) {
1293                // this is undocumented as it doesn't work right.
1294                suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
1295                if (t.hasMoreTokens()) {
1296                    modif = t.nextToken();
1297                }
1298            }
1299
1300            if  (modif.equals("method")) {
1301                String traceCmd = null;
1302
1303                if (t.hasMoreTokens()) {
1304                    String modif1 = t.nextToken();
1305                    if (modif1.equals("exits") || modif1.equals("exit")) {
1306                        if (t.hasMoreTokens()) {
1307                            threadInfo = doGetThread(t.nextToken());
1308                        }
1309                        if (modif1.equals("exit")) {
1310                            StackFrame frame;
1311                            try {
1312                                frame = ThreadInfo.getCurrentThreadInfo().getCurrentFrame();
1313                            } catch (IncompatibleThreadStateException ee) {
1314                                MessageOutput.println("Current thread isnt suspended.");
1315                                return;
1316                            }
1317                            Env.setAtExitMethod(frame.location().method());
1318                            traceCmd = MessageOutput.format("trace" +
1319                                                    goStr + "method exit " +
1320                                                    "in effect for",
1321                                                    Env.atExitMethod().toString());
1322                        } else {
1323                            traceCmd = MessageOutput.format("trace" +
1324                                                   goStr + "method exits " +
1325                                                   "in effect");
1326                        }
1327                        commandUntrace(new StringTokenizer("methods"));
1328                        turnOnExitTrace(threadInfo, suspendPolicy);
1329                        methodTraceCommand = traceCmd;
1330                        return;
1331                    }
1332                } else {
1333                   MessageOutput.println("Can only trace");
1334                   return;
1335                }
1336            }
1337            if (modif.equals("methods")) {
1338                // Turn on method entry trace
1339                MethodEntryRequest entry;
1340                EventRequestManager erm = Env.vm().eventRequestManager();
1341                if (t.hasMoreTokens()) {
1342                    threadInfo = doGetThread(t.nextToken());
1343                }
1344                if (threadInfo != null) {
1345                    /*
1346                     * To keep things simple we want each 'trace' to cancel
1347                     * previous traces.  However in this case, we don't do that
1348                     * to preserve backward compatibility with pre JDK 6.0.
1349                     * IE, you can currently do
1350                     *   trace   methods 0x21
1351                     *   trace   methods 0x22
1352                     * and you will get xxx traced just on those two threads
1353                     * But this feature is kind of broken because if you then do
1354                     *   untrace  0x21
1355                     * it turns off both traces instead of just the one.
1356                     * Another bogosity is that if you do
1357                     *   trace methods
1358                     *   trace methods
1359                     * and you will get two traces.
1360                     */
1361
1362                    entry = erm.createMethodEntryRequest();
1363                    entry.addThreadFilter(threadInfo.getThread());
1364                } else {
1365                    commandUntrace(new StringTokenizer("methods"));
1366                    entry = erm.createMethodEntryRequest();
1367                }
1368                Env.addExcludes(entry);
1369                entry.setSuspendPolicy(suspendPolicy);
1370                entry.enable();
1371                turnOnExitTrace(threadInfo, suspendPolicy);
1372                methodTraceCommand = MessageOutput.format("trace" + goStr +
1373                                                          "methods in effect");
1374
1375                return;
1376            }
1377
1378            MessageOutput.println("Can only trace");
1379            return;
1380        }
1381
1382        // trace all by itself.
1383        if (methodTraceCommand != null) {
1384            MessageOutput.printDirectln(methodTraceCommand);
1385        }
1386
1387        // More trace lines can be added here.
1388    }
1389
1390    void commandUntrace(StringTokenizer t) {
1391        // untrace
1392        // untrace methods
1393
1394        String modif = null;
1395        EventRequestManager erm = Env.vm().eventRequestManager();
1396        if (t.hasMoreTokens()) {
1397            modif = t.nextToken();
1398        }
1399        if (modif == null || modif.equals("methods")) {
1400            erm.deleteEventRequests(erm.methodEntryRequests());
1401            erm.deleteEventRequests(erm.methodExitRequests());
1402            Env.setAtExitMethod(null);
1403            methodTraceCommand = null;
1404        }
1405    }
1406
1407    void commandList(StringTokenizer t) {
1408        StackFrame frame = null;
1409        ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1410        if (threadInfo == null) {
1411            MessageOutput.println("No thread specified.");
1412            return;
1413        }
1414        try {
1415            frame = threadInfo.getCurrentFrame();
1416        } catch (IncompatibleThreadStateException e) {
1417            MessageOutput.println("Current thread isnt suspended.");
1418            return;
1419        }
1420
1421        if (frame == null) {
1422            MessageOutput.println("No frames on the current call stack");
1423            return;
1424        }
1425
1426        Location loc = frame.location();
1427        if (loc.method().isNative()) {
1428            MessageOutput.println("Current method is native");
1429            return;
1430        }
1431
1432        String sourceFileName = null;
1433        try {
1434            sourceFileName = loc.sourceName();
1435
1436            ReferenceType refType = loc.declaringType();
1437            int lineno = loc.lineNumber();
1438
1439            if (t.hasMoreTokens()) {
1440                String id = t.nextToken();
1441
1442                // See if token is a line number.
1443                try {
1444                    NumberFormat nf = NumberFormat.getNumberInstance();
1445                    nf.setParseIntegerOnly(true);
1446                    Number n = nf.parse(id);
1447                    lineno = n.intValue();
1448                } catch (java.text.ParseException jtpe) {
1449                    // It isn't -- see if it's a method name.
1450                        List<Method> meths = refType.methodsByName(id);
1451                        if (meths == null || meths.size() == 0) {
1452                            MessageOutput.println("is not a valid line number or method name for",
1453                                                  new Object [] {id, refType.name()});
1454                            return;
1455                        } else if (meths.size() > 1) {
1456                            MessageOutput.println("is an ambiguous method name in",
1457                                                  new Object [] {id, refType.name()});
1458                            return;
1459                        }
1460                        loc = meths.get(0).location();
1461                        lineno = loc.lineNumber();
1462                }
1463            }
1464            int startLine = Math.max(lineno - 4, 1);
1465            int endLine = startLine + 9;
1466            if (lineno < 0) {
1467                MessageOutput.println("Line number information not available for");
1468            } else if (Env.sourceLine(loc, lineno) == null) {
1469                MessageOutput.println("is an invalid line number for",
1470                                      new Object [] {Integer.valueOf(lineno),
1471                                                     refType.name()});
1472            } else {
1473                for (int i = startLine; i <= endLine; i++) {
1474                    String sourceLine = Env.sourceLine(loc, i);
1475                    if (sourceLine == null) {
1476                        break;
1477                    }
1478                    if (i == lineno) {
1479                        MessageOutput.println("source line number current line and line",
1480                                              new Object [] {Integer.valueOf(i),
1481                                                             sourceLine});
1482                    } else {
1483                        MessageOutput.println("source line number and line",
1484                                              new Object [] {Integer.valueOf(i),
1485                                                             sourceLine});
1486                    }
1487                }
1488            }
1489        } catch (AbsentInformationException e) {
1490            MessageOutput.println("No source information available for:", loc.toString());
1491        } catch(FileNotFoundException exc) {
1492            MessageOutput.println("Source file not found:", sourceFileName);
1493        } catch(IOException exc) {
1494            MessageOutput.println("I/O exception occurred:", exc.toString());
1495        }
1496    }
1497
1498    void commandLines(StringTokenizer t) { // Undocumented command: useful for testing
1499        if (!t.hasMoreTokens()) {
1500            MessageOutput.println("Specify class and method");
1501        } else {
1502            String idClass = t.nextToken();
1503            String idMethod = t.hasMoreTokens() ? t.nextToken() : null;
1504            try {
1505                ReferenceType refType = Env.getReferenceTypeFromToken(idClass);
1506                if (refType != null) {
1507                    List<Location> lines = null;
1508                    if (idMethod == null) {
1509                        lines = refType.allLineLocations();
1510                    } else {
1511                        for (Method method : refType.allMethods()) {
1512                            if (method.name().equals(idMethod)) {
1513                                lines = method.allLineLocations();
1514                            }
1515                        }
1516                        if (lines == null) {
1517                            MessageOutput.println("is not a valid method name", idMethod);
1518                        }
1519                    }
1520                    for (Location line : lines) {
1521                        MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
1522                    }
1523                } else {
1524                    MessageOutput.println("is not a valid id or class name", idClass);
1525                }
1526            } catch (AbsentInformationException e) {
1527                MessageOutput.println("Line number information not available for", idClass);
1528            }
1529        }
1530    }
1531
1532    void commandClasspath(StringTokenizer t) {
1533        if (Env.vm() instanceof PathSearchingVirtualMachine) {
1534            PathSearchingVirtualMachine vm = (PathSearchingVirtualMachine)Env.vm();
1535            MessageOutput.println("base directory:", vm.baseDirectory());
1536            MessageOutput.println("classpath:", vm.classPath().toString());
1537        } else {
1538            MessageOutput.println("The VM does not use paths");
1539        }
1540    }
1541
1542    /* Get or set the source file path list. */
1543    void commandUse(StringTokenizer t) {
1544        if (!t.hasMoreTokens()) {
1545            MessageOutput.printDirectln(Env.getSourcePath());// Special case: use printDirectln()
1546        } else {
1547            /*
1548             * Take the remainder of the command line, minus
1549             * leading or trailing whitespace.  Embedded
1550             * whitespace is fine.
1551             */
1552            Env.setSourcePath(t.nextToken("").trim());
1553        }
1554    }
1555
1556    /* Print a stack variable */
1557    private void printVar(LocalVariable var, Value value) {
1558        MessageOutput.println("expr is value",
1559                              new Object [] {var.name(),
1560                                             value == null ? "null" : value.toString()});
1561    }
1562
1563    /* Print all local variables in current stack frame. */
1564    void commandLocals() {
1565        StackFrame frame;
1566        ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1567        if (threadInfo == null) {
1568            MessageOutput.println("No default thread specified:");
1569            return;
1570        }
1571        try {
1572            frame = threadInfo.getCurrentFrame();
1573            if (frame == null) {
1574                throw new AbsentInformationException();
1575            }
1576            List<LocalVariable> vars = frame.visibleVariables();
1577
1578            if (vars.size() == 0) {
1579                MessageOutput.println("No local variables");
1580                return;
1581            }
1582            Map<LocalVariable, Value> values = frame.getValues(vars);
1583
1584            MessageOutput.println("Method arguments:");
1585            for (LocalVariable var : vars) {
1586                if (var.isArgument()) {
1587                    Value val = values.get(var);
1588                    printVar(var, val);
1589                }
1590            }
1591            MessageOutput.println("Local variables:");
1592            for (LocalVariable var : vars) {
1593                if (!var.isArgument()) {
1594                    Value val = values.get(var);
1595                    printVar(var, val);
1596                }
1597            }
1598        } catch (AbsentInformationException aie) {
1599            MessageOutput.println("Local variable information not available.");
1600        } catch (IncompatibleThreadStateException exc) {
1601            MessageOutput.println("Current thread isnt suspended.");
1602        }
1603    }
1604
1605    private void dump(ObjectReference obj, ReferenceType refType,
1606                      ReferenceType refTypeBase) {
1607        for (Field field : refType.fields()) {
1608            StringBuilder sb = new StringBuilder();
1609            sb.append("    ");
1610            if (!refType.equals(refTypeBase)) {
1611                sb.append(refType.name());
1612                sb.append(".");
1613            }
1614            sb.append(field.name());
1615            sb.append(MessageOutput.format("colon space"));
1616            sb.append(obj.getValue(field));
1617            MessageOutput.printDirectln(sb.toString()); // Special case: use printDirectln()
1618        }
1619        if (refType instanceof ClassType) {
1620            ClassType sup = ((ClassType)refType).superclass();
1621            if (sup != null) {
1622                dump(obj, sup, refTypeBase);
1623            }
1624        } else if (refType instanceof InterfaceType) {
1625            for (InterfaceType sup : ((InterfaceType)refType).superinterfaces()) {
1626                dump(obj, sup, refTypeBase);
1627            }
1628        } else {
1629            /* else refType is an instanceof ArrayType */
1630            if (obj instanceof ArrayReference) {
1631                for (Iterator<Value> it = ((ArrayReference)obj).getValues().iterator();
1632                     it.hasNext(); ) {
1633                    MessageOutput.printDirect(it.next().toString());// Special case: use printDirect()
1634                    if (it.hasNext()) {
1635                        MessageOutput.printDirect(", ");// Special case: use printDirect()
1636                    }
1637                }
1638                MessageOutput.println();
1639            }
1640        }
1641    }
1642
1643    /* Print a specified reference.
1644     */
1645    void doPrint(StringTokenizer t, boolean dumpObject) {
1646        if (!t.hasMoreTokens()) {
1647            MessageOutput.println("No objects specified.");
1648            return;
1649        }
1650
1651        while (t.hasMoreTokens()) {
1652            String expr = t.nextToken("");
1653            Value val = evaluate(expr);
1654            if (val == null) {
1655                MessageOutput.println("expr is null", expr.toString());
1656            } else if (dumpObject && (val instanceof ObjectReference) &&
1657                       !(val instanceof StringReference)) {
1658                ObjectReference obj = (ObjectReference)val;
1659                ReferenceType refType = obj.referenceType();
1660                MessageOutput.println("expr is value",
1661                                      new Object [] {expr.toString(),
1662                                                     MessageOutput.format("grouping begin character")});
1663                dump(obj, refType, refType);
1664                MessageOutput.println("grouping end character");
1665            } else {
1666                  String strVal = getStringValue();
1667                  if (strVal != null) {
1668                     MessageOutput.println("expr is value", new Object [] {expr.toString(),
1669                                                                      strVal});
1670                   }
1671            }
1672        }
1673    }
1674
1675    void commandPrint(final StringTokenizer t, final boolean dumpObject) {
1676        new AsyncExecution() {
1677                @Override
1678                void action() {
1679                    doPrint(t, dumpObject);
1680                }
1681            };
1682    }
1683
1684    void commandSet(final StringTokenizer t) {
1685        String all = t.nextToken("");
1686
1687        /*
1688         * Bare bones error checking.
1689         */
1690        if (all.indexOf('=') == -1) {
1691            MessageOutput.println("Invalid assignment syntax");
1692            MessageOutput.printPrompt();
1693            return;
1694        }
1695
1696        /*
1697         * The set command is really just syntactic sugar. Pass it on to the
1698         * print command.
1699         */
1700        commandPrint(new StringTokenizer(all), false);
1701    }
1702
1703    void doLock(StringTokenizer t) {
1704        if (!t.hasMoreTokens()) {
1705            MessageOutput.println("No object specified.");
1706            return;
1707        }
1708
1709        String expr = t.nextToken("");
1710        Value val = evaluate(expr);
1711
1712        try {
1713            if ((val != null) && (val instanceof ObjectReference)) {
1714                ObjectReference object = (ObjectReference)val;
1715                String strVal = getStringValue();
1716                if (strVal != null) {
1717                    MessageOutput.println("Monitor information for expr",
1718                                      new Object [] {expr.trim(),
1719                                                     strVal});
1720                }
1721                ThreadReference owner = object.owningThread();
1722                if (owner == null) {
1723                    MessageOutput.println("Not owned");
1724                } else {
1725                    MessageOutput.println("Owned by:",
1726                                          new Object [] {owner.name(),
1727                                                         Integer.valueOf(object.entryCount())});
1728                }
1729                List<ThreadReference> waiters = object.waitingThreads();
1730                if (waiters.size() == 0) {
1731                    MessageOutput.println("No waiters");
1732                } else {
1733                    for (ThreadReference waiter : waiters) {
1734                        MessageOutput.println("Waiting thread:", waiter.name());
1735                    }
1736                }
1737            } else {
1738                MessageOutput.println("Expression must evaluate to an object");
1739            }
1740        } catch (IncompatibleThreadStateException e) {
1741            MessageOutput.println("Threads must be suspended");
1742        }
1743    }
1744
1745    void commandLock(final StringTokenizer t) {
1746        new AsyncExecution() {
1747                @Override
1748                void action() {
1749                    doLock(t);
1750                }
1751            };
1752    }
1753
1754    private void printThreadLockInfo(ThreadInfo threadInfo) {
1755        ThreadReference thread = threadInfo.getThread();
1756        try {
1757            MessageOutput.println("Monitor information for thread", thread.name());
1758            List<ObjectReference> owned = thread.ownedMonitors();
1759            if (owned.size() == 0) {
1760                MessageOutput.println("No monitors owned");
1761            } else {
1762                for (ObjectReference monitor : owned) {
1763                    MessageOutput.println("Owned monitor:", monitor.toString());
1764                }
1765            }
1766            ObjectReference waiting = thread.currentContendedMonitor();
1767            if (waiting == null) {
1768                MessageOutput.println("Not waiting for a monitor");
1769            } else {
1770                MessageOutput.println("Waiting for monitor:", waiting.toString());
1771            }
1772        } catch (IncompatibleThreadStateException e) {
1773            MessageOutput.println("Threads must be suspended");
1774        }
1775    }
1776
1777    void commandThreadlocks(final StringTokenizer t) {
1778        if (!t.hasMoreTokens()) {
1779            ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1780            if (threadInfo == null) {
1781                MessageOutput.println("Current thread not set.");
1782            } else {
1783                printThreadLockInfo(threadInfo);
1784            }
1785            return;
1786        }
1787        String token = t.nextToken();
1788        if (token.toLowerCase().equals("all")) {
1789            for (ThreadInfo threadInfo : ThreadInfo.threads()) {
1790                printThreadLockInfo(threadInfo);
1791            }
1792        } else {
1793            ThreadInfo threadInfo = doGetThread(token);
1794            if (threadInfo != null) {
1795                ThreadInfo.setCurrentThreadInfo(threadInfo);
1796                printThreadLockInfo(threadInfo);
1797            }
1798        }
1799    }
1800
1801    void doDisableGC(StringTokenizer t) {
1802        if (!t.hasMoreTokens()) {
1803            MessageOutput.println("No object specified.");
1804            return;
1805        }
1806
1807        String expr = t.nextToken("");
1808        Value val = evaluate(expr);
1809        if ((val != null) && (val instanceof ObjectReference)) {
1810            ObjectReference object = (ObjectReference)val;
1811            object.disableCollection();
1812            String strVal = getStringValue();
1813            if (strVal != null) {
1814                 MessageOutput.println("GC Disabled for", strVal);
1815            }
1816        } else {
1817            MessageOutput.println("Expression must evaluate to an object");
1818        }
1819    }
1820
1821    void commandDisableGC(final StringTokenizer t) {
1822        new AsyncExecution() {
1823                @Override
1824                void action() {
1825                    doDisableGC(t);
1826                }
1827            };
1828    }
1829
1830    void doEnableGC(StringTokenizer t) {
1831        if (!t.hasMoreTokens()) {
1832            MessageOutput.println("No object specified.");
1833            return;
1834        }
1835
1836        String expr = t.nextToken("");
1837        Value val = evaluate(expr);
1838        if ((val != null) && (val instanceof ObjectReference)) {
1839            ObjectReference object = (ObjectReference)val;
1840            object.enableCollection();
1841            String strVal = getStringValue();
1842            if (strVal != null) {
1843                 MessageOutput.println("GC Enabled for", strVal);
1844            }
1845        } else {
1846            MessageOutput.println("Expression must evaluate to an object");
1847        }
1848    }
1849
1850    void commandEnableGC(final StringTokenizer t) {
1851        new AsyncExecution() {
1852                @Override
1853                void action() {
1854                    doEnableGC(t);
1855                }
1856            };
1857    }
1858
1859    void doSave(StringTokenizer t) {// Undocumented command: useful for testing.
1860        if (!t.hasMoreTokens()) {
1861            MessageOutput.println("No save index specified.");
1862            return;
1863        }
1864
1865        String key = t.nextToken();
1866
1867        if (!t.hasMoreTokens()) {
1868            MessageOutput.println("No expression specified.");
1869            return;
1870        }
1871        String expr = t.nextToken("");
1872        Value val = evaluate(expr);
1873        if (val != null) {
1874            Env.setSavedValue(key, val);
1875            String strVal = getStringValue();
1876            if (strVal != null) {
1877                 MessageOutput.println("saved", strVal);
1878            }
1879        } else {
1880            MessageOutput.println("Expression cannot be void");
1881        }
1882    }
1883
1884    void commandSave(final StringTokenizer t) { // Undocumented command: useful for testing.
1885        if (!t.hasMoreTokens()) {
1886            Set<String> keys = Env.getSaveKeys();
1887            if (keys.isEmpty()) {
1888                MessageOutput.println("No saved values");
1889                return;
1890            }
1891            for (String key : keys) {
1892                Value value = Env.getSavedValue(key);
1893                if ((value instanceof ObjectReference) &&
1894                    ((ObjectReference)value).isCollected()) {
1895                    MessageOutput.println("expr is value <collected>",
1896                                          new Object [] {key, value.toString()});
1897                } else {
1898                    if (value == null){
1899                        MessageOutput.println("expr is null", key);
1900                    } else {
1901                        MessageOutput.println("expr is value",
1902                                              new Object [] {key, value.toString()});
1903                    }
1904                }
1905            }
1906        } else {
1907            new AsyncExecution() {
1908                    @Override
1909                    void action() {
1910                        doSave(t);
1911                    }
1912                };
1913        }
1914
1915    }
1916
1917   void commandBytecodes(final StringTokenizer t) { // Undocumented command: useful for testing.
1918        if (!t.hasMoreTokens()) {
1919            MessageOutput.println("No class specified.");
1920            return;
1921        }
1922        String className = t.nextToken();
1923
1924        if (!t.hasMoreTokens()) {
1925            MessageOutput.println("No method specified.");
1926            return;
1927        }
1928        // Overloading is not handled here.
1929        String methodName = t.nextToken();
1930
1931        List<ReferenceType> classes = Env.vm().classesByName(className);
1932        // TO DO: handle multiple classes found
1933        if (classes.size() == 0) {
1934            if (className.indexOf('.') < 0) {
1935                MessageOutput.println("not found (try the full name)", className);
1936            } else {
1937                MessageOutput.println("not found", className);
1938            }
1939            return;
1940        }
1941
1942        ReferenceType rt = classes.get(0);
1943        if (!(rt instanceof ClassType)) {
1944            MessageOutput.println("not a class", className);
1945            return;
1946        }
1947
1948        byte[] bytecodes = null;
1949        for (Method method : rt.methodsByName(methodName)) {
1950            if (!method.isAbstract()) {
1951                bytecodes = method.bytecodes();
1952                break;
1953            }
1954        }
1955
1956        StringBuilder line = new StringBuilder(80);
1957        line.append("0000: ");
1958        for (int i = 0; i < bytecodes.length; i++) {
1959            if ((i > 0) && (i % 16 == 0)) {
1960                MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
1961                line.setLength(0);
1962                line.append(String.valueOf(i));
1963                line.append(": ");
1964                int len = line.length();
1965                for (int j = 0; j < 6 - len; j++) {
1966                    line.insert(0, '0');
1967                }
1968            }
1969            int val = 0xff & bytecodes[i];
1970            String str = Integer.toHexString(val);
1971            if (str.length() == 1) {
1972                line.append('0');
1973            }
1974            line.append(str);
1975            line.append(' ');
1976        }
1977        if (line.length() > 6) {
1978            MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
1979        }
1980    }
1981
1982    void commandExclude(StringTokenizer t) {
1983        if (!t.hasMoreTokens()) {
1984            MessageOutput.printDirectln(Env.excludesString());// Special case: use printDirectln()
1985        } else {
1986            String rest = t.nextToken("");
1987            if (rest.equals("none")) {
1988                rest = "";
1989            }
1990            Env.setExcludes(rest);
1991        }
1992    }
1993
1994    void commandRedefine(StringTokenizer t) {
1995        if (!t.hasMoreTokens()) {
1996            MessageOutput.println("Specify classes to redefine");
1997        } else {
1998            String className = t.nextToken();
1999            List<ReferenceType> classes = Env.vm().classesByName(className);
2000            if (classes.size() == 0) {
2001                MessageOutput.println("No class named", className);
2002                return;
2003            }
2004            if (classes.size() > 1) {
2005                MessageOutput.println("More than one class named", className);
2006                return;
2007            }
2008            Env.setSourcePath(Env.getSourcePath());
2009            ReferenceType refType = classes.get(0);
2010            if (!t.hasMoreTokens()) {
2011                MessageOutput.println("Specify file name for class", className);
2012                return;
2013            }
2014            String fileName = t.nextToken();
2015            File phyl = new File(fileName);
2016            byte[] bytes = new byte[(int)phyl.length()];
2017            try {
2018                InputStream in = new FileInputStream(phyl);
2019                in.read(bytes);
2020                in.close();
2021            } catch (Exception exc) {
2022                MessageOutput.println("Error reading file",
2023                             new Object [] {fileName, exc.toString()});
2024                return;
2025            }
2026            Map<ReferenceType, byte[]> map
2027                = new HashMap<ReferenceType, byte[]>();
2028            map.put(refType, bytes);
2029            try {
2030                Env.vm().redefineClasses(map);
2031            } catch (Throwable exc) {
2032                MessageOutput.println("Error redefining class to file",
2033                             new Object [] {className,
2034                                            fileName,
2035                                            exc});
2036            }
2037        }
2038    }
2039
2040    void commandPopFrames(StringTokenizer t, boolean reenter) {
2041        ThreadInfo threadInfo;
2042
2043        if (t.hasMoreTokens()) {
2044            String token = t.nextToken();
2045            threadInfo = doGetThread(token);
2046            if (threadInfo == null) {
2047                return;
2048            }
2049        } else {
2050            threadInfo = ThreadInfo.getCurrentThreadInfo();
2051            if (threadInfo == null) {
2052                MessageOutput.println("No thread specified.");
2053                return;
2054            }
2055        }
2056
2057        try {
2058            StackFrame frame = threadInfo.getCurrentFrame();
2059            threadInfo.getThread().popFrames(frame);
2060            threadInfo = ThreadInfo.getCurrentThreadInfo();
2061            ThreadInfo.setCurrentThreadInfo(threadInfo);
2062            if (reenter) {
2063                commandStepi();
2064            }
2065        } catch (Throwable exc) {
2066            MessageOutput.println("Error popping frame", exc.toString());
2067        }
2068    }
2069
2070    void commandExtension(StringTokenizer t) {
2071        if (!t.hasMoreTokens()) {
2072            MessageOutput.println("No class specified.");
2073            return;
2074        }
2075
2076        String idClass = t.nextToken();
2077        ReferenceType cls = Env.getReferenceTypeFromToken(idClass);
2078        String extension = null;
2079        if (cls != null) {
2080            try {
2081                extension = cls.sourceDebugExtension();
2082                MessageOutput.println("sourcedebugextension", extension);
2083            } catch (AbsentInformationException e) {
2084                MessageOutput.println("No sourcedebugextension specified");
2085            }
2086        } else {
2087            MessageOutput.println("is not a valid id or class name", idClass);
2088        }
2089    }
2090
2091    void commandVersion(String debuggerName,
2092                        VirtualMachineManager vmm) {
2093        MessageOutput.println("minus version",
2094                              new Object [] { debuggerName,
2095                                              vmm.majorInterfaceVersion(),
2096                                              vmm.minorInterfaceVersion(),
2097                                                  System.getProperty("java.version")});
2098        if (Env.connection() != null) {
2099            try {
2100                MessageOutput.printDirectln(Env.vm().description());// Special case: use printDirectln()
2101            } catch (VMNotConnectedException e) {
2102                MessageOutput.println("No VM connected");
2103            }
2104        }
2105    }
2106}
2107