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 
  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 {
  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 []{new Integer(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 [] { new Integer (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 [] {new Integer(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 [] {new Integer(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                                                    new Integer (loc.lineNumber()),
1018                                                    new Long (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 [] {new Integer (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 [] {new Integer (i),
1481                                                              sourceLine});
1482                     } else {
1483                         MessageOutput.println("source line number and line",
1484                                               new Object [] {new Integer (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             MessageOutput.println("bootclasspath:", vm.bootClassPath().toString());
1538         } else {
1539             MessageOutput.println("The VM does not use paths");
1540         }
1541     }
1542 
1543     /* Get or set the source file path list. */
1544     void commandUse(StringTokenizer t) {
1545         if (!t.hasMoreTokens()) {
1546             MessageOutput.printDirectln(Env.getSourcePath());// Special case: use printDirectln()
1547         } else {
1548             /*
1549              * Take the remainder of the command line, minus
1550              * leading or trailing whitespace.  Embedded
1551              * whitespace is fine.
1552              */
1553             Env.setSourcePath(t.nextToken("").trim());
1554         }
1555     }
1556 
1557     /* Print a stack variable */
1558     private void printVar(LocalVariable var, Value value) {
1559         MessageOutput.println("expr is value",
1560                               new Object [] {var.name(),
1561                                              value == null ? "null" : value.toString()});
1562     }
1563 
1564     /* Print all local variables in current stack frame. */
1565     void commandLocals() {
1566         StackFrame frame;
1567         ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1568         if (threadInfo == null) {
1569             MessageOutput.println("No default thread specified:");
1570             return;
1571         }
1572         try {
1573             frame = threadInfo.getCurrentFrame();
1574             if (frame == null) {
1575                 throw new AbsentInformationException();
1576             }
1577             List<LocalVariable> vars = frame.visibleVariables();
1578 
1579             if (vars.size() == 0) {
1580                 MessageOutput.println("No local variables");
1581                 return;
1582             }
1583             Map<LocalVariable, Value> values = frame.getValues(vars);
1584 
1585             MessageOutput.println("Method arguments:");
1586             for (LocalVariable var : vars) {
1587                 if (var.isArgument()) {
1588                     Value val = values.get(var);
1589                     printVar(var, val);
1590                 }
1591             }
1592             MessageOutput.println("Local variables:");
1593             for (LocalVariable var : vars) {
1594                 if (!var.isArgument()) {
1595                     Value val = values.get(var);
1596                     printVar(var, val);
1597                 }
1598             }
1599         } catch (AbsentInformationException aie) {
1600             MessageOutput.println("Local variable information not available.");
1601         } catch (IncompatibleThreadStateException exc) {
1602             MessageOutput.println("Current thread isnt suspended.");
1603         }
1604     }
1605 
1606     private void dump(ObjectReference obj, ReferenceType refType,
1607                       ReferenceType refTypeBase) {
1608         for (Field field : refType.fields()) {
1609             StringBuilder sb = new StringBuilder();
1610             sb.append("    ");
1611             if (!refType.equals(refTypeBase)) {
1612                 sb.append(refType.name());
1613                 sb.append(".");
1614             }
1615             sb.append(field.name());
1616             sb.append(MessageOutput.format("colon space"));
1617             sb.append(obj.getValue(field));
1618             MessageOutput.printDirectln(sb.toString()); // Special case: use printDirectln()
1619         }
1620         if (refType instanceof ClassType) {
1621             ClassType sup = ((ClassType)refType).superclass();
1622             if (sup != null) {
1623                 dump(obj, sup, refTypeBase);
1624             }
1625         } else if (refType instanceof InterfaceType) {
1626             for (InterfaceType sup : ((InterfaceType)refType).superinterfaces()) {
1627                 dump(obj, sup, refTypeBase);
1628             }
1629         } else {
1630             /* else refType is an instanceof ArrayType */
1631             if (obj instanceof ArrayReference) {
1632                 for (Iterator<Value> it = ((ArrayReference)obj).getValues().iterator();
1633                      it.hasNext(); ) {
1634                     MessageOutput.printDirect(it.next().toString());// Special case: use printDirect()
1635                     if (it.hasNext()) {
1636                         MessageOutput.printDirect(", ");// Special case: use printDirect()
1637                     }
1638                 }
1639                 MessageOutput.println();
1640             }
1641         }
1642     }
1643 
1644     /* Print a specified reference.
1645      */
1646     void doPrint(StringTokenizer t, boolean dumpObject) {
1647         if (!t.hasMoreTokens()) {
1648             MessageOutput.println("No objects specified.");
1649             return;
1650         }
1651 
1652         while (t.hasMoreTokens()) {
1653             String expr = t.nextToken("");
1654             Value val = evaluate(expr);
1655             if (val == null) {
1656                 MessageOutput.println("expr is null", expr.toString());
1657             } else if (dumpObject && (val instanceof ObjectReference) &&
1658                        !(val instanceof StringReference)) {
1659                 ObjectReference obj = (ObjectReference)val;
1660                 ReferenceType refType = obj.referenceType();
1661                 MessageOutput.println("expr is value",
1662                                       new Object [] {expr.toString(),
1663                                                      MessageOutput.format("grouping begin character")});
1664                 dump(obj, refType, refType);
1665                 MessageOutput.println("grouping end character");
1666             } else {
1667                   String strVal = getStringValue();
1668                   if (strVal != null) {
1669                      MessageOutput.println("expr is value", new Object [] {expr.toString(),
1670                                                                       strVal});
1671                    }
1672             }
1673         }
1674     }
1675 
1676     void commandPrint(final StringTokenizer t, final boolean dumpObject) {
1677         new AsyncExecution() {
1678                 @Override
1679                 void action() {
1680                     doPrint(t, dumpObject);
1681                 }
1682             };
1683     }
1684 
1685     void commandSet(final StringTokenizer t) {
1686         String all = t.nextToken("");
1687 
1688         /*
1689          * Bare bones error checking.
1690          */
1691         if (all.indexOf('=') == -1) {
1692             MessageOutput.println("Invalid assignment syntax");
1693             MessageOutput.printPrompt();
1694             return;
1695         }
1696 
1697         /*
1698          * The set command is really just syntactic sugar. Pass it on to the
1699          * print command.
1700          */
1701         commandPrint(new StringTokenizer(all), false);
1702     }
1703 
1704     void doLock(StringTokenizer t) {
1705         if (!t.hasMoreTokens()) {
1706             MessageOutput.println("No object specified.");
1707             return;
1708         }
1709 
1710         String expr = t.nextToken("");
1711         Value val = evaluate(expr);
1712 
1713         try {
1714             if ((val != null) && (val instanceof ObjectReference)) {
1715                 ObjectReference object = (ObjectReference)val;
1716                 String strVal = getStringValue();
1717                 if (strVal != null) {
1718                     MessageOutput.println("Monitor information for expr",
1719                                       new Object [] {expr.trim(),
1720                                                      strVal});
1721                 }
1722                 ThreadReference owner = object.owningThread();
1723                 if (owner == null) {
1724                     MessageOutput.println("Not owned");
1725                 } else {
1726                     MessageOutput.println("Owned by:",
1727                                           new Object [] {owner.name(),
1728                                                          new Integer (object.entryCount())});
1729                 }
1730                 List<ThreadReference> waiters = object.waitingThreads();
1731                 if (waiters.size() == 0) {
1732                     MessageOutput.println("No waiters");
1733                 } else {
1734                     for (ThreadReference waiter : waiters) {
1735                         MessageOutput.println("Waiting thread:", waiter.name());
1736                     }
1737                 }
1738             } else {
1739                 MessageOutput.println("Expression must evaluate to an object");
1740             }
1741         } catch (IncompatibleThreadStateException e) {
1742             MessageOutput.println("Threads must be suspended");
1743         }
1744     }
1745 
1746     void commandLock(final StringTokenizer t) {
1747         new AsyncExecution() {
1748                 @Override
1749                 void action() {
1750                     doLock(t);
1751                 }
1752             };
1753     }
1754 
1755     private void printThreadLockInfo(ThreadInfo threadInfo) {
1756         ThreadReference thread = threadInfo.getThread();
1757         try {
1758             MessageOutput.println("Monitor information for thread", thread.name());
1759             List<ObjectReference> owned = thread.ownedMonitors();
1760             if (owned.size() == 0) {
1761                 MessageOutput.println("No monitors owned");
1762             } else {
1763                 for (ObjectReference monitor : owned) {
1764                     MessageOutput.println("Owned monitor:", monitor.toString());
1765                 }
1766             }
1767             ObjectReference waiting = thread.currentContendedMonitor();
1768             if (waiting == null) {
1769                 MessageOutput.println("Not waiting for a monitor");
1770             } else {
1771                 MessageOutput.println("Waiting for monitor:", waiting.toString());
1772             }
1773         } catch (IncompatibleThreadStateException e) {
1774             MessageOutput.println("Threads must be suspended");
1775         }
1776     }
1777 
1778     void commandThreadlocks(final StringTokenizer t) {
1779         if (!t.hasMoreTokens()) {
1780             ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo();
1781             if (threadInfo == null) {
1782                 MessageOutput.println("Current thread not set.");
1783             } else {
1784                 printThreadLockInfo(threadInfo);
1785             }
1786             return;
1787         }
1788         String token = t.nextToken();
1789         if (token.toLowerCase().equals("all")) {
1790             for (ThreadInfo threadInfo : ThreadInfo.threads()) {
1791                 printThreadLockInfo(threadInfo);
1792             }
1793         } else {
1794             ThreadInfo threadInfo = doGetThread(token);
1795             if (threadInfo != null) {
1796                 ThreadInfo.setCurrentThreadInfo(threadInfo);
1797                 printThreadLockInfo(threadInfo);
1798             }
1799         }
1800     }
1801 
1802     void doDisableGC(StringTokenizer t) {
1803         if (!t.hasMoreTokens()) {
1804             MessageOutput.println("No object specified.");
1805             return;
1806         }
1807 
1808         String expr = t.nextToken("");
1809         Value val = evaluate(expr);
1810         if ((val != null) && (val instanceof ObjectReference)) {
1811             ObjectReference object = (ObjectReference)val;
1812             object.disableCollection();
1813             String strVal = getStringValue();
1814             if (strVal != null) {
1815                  MessageOutput.println("GC Disabled for", strVal);
1816             }
1817         } else {
1818             MessageOutput.println("Expression must evaluate to an object");
1819         }
1820     }
1821 
1822     void commandDisableGC(final StringTokenizer t) {
1823         new AsyncExecution() {
1824                 @Override
1825                 void action() {
1826                     doDisableGC(t);
1827                 }
1828             };
1829     }
1830 
1831     void doEnableGC(StringTokenizer t) {
1832         if (!t.hasMoreTokens()) {
1833             MessageOutput.println("No object specified.");
1834             return;
1835         }
1836 
1837         String expr = t.nextToken("");
1838         Value val = evaluate(expr);
1839         if ((val != null) && (val instanceof ObjectReference)) {
1840             ObjectReference object = (ObjectReference)val;
1841             object.enableCollection();
1842             String strVal = getStringValue();
1843             if (strVal != null) {
1844                  MessageOutput.println("GC Enabled for", strVal);
1845             }
1846         } else {
1847             MessageOutput.println("Expression must evaluate to an object");
1848         }
1849     }
1850 
1851     void commandEnableGC(final StringTokenizer t) {
1852         new AsyncExecution() {
1853                 @Override
1854                 void action() {
1855                     doEnableGC(t);
1856                 }
1857             };
1858     }
1859 
1860     void doSave(StringTokenizer t) {// Undocumented command: useful for testing.
1861         if (!t.hasMoreTokens()) {
1862             MessageOutput.println("No save index specified.");
1863             return;
1864         }
1865 
1866         String key = t.nextToken();
1867 
1868         if (!t.hasMoreTokens()) {
1869             MessageOutput.println("No expression specified.");
1870             return;
1871         }
1872         String expr = t.nextToken("");
1873         Value val = evaluate(expr);
1874         if (val != null) {
1875             Env.setSavedValue(key, val);
1876             String strVal = getStringValue();
1877             if (strVal != null) {
1878                  MessageOutput.println("saved", strVal);
1879             }
1880         } else {
1881             MessageOutput.println("Expression cannot be void");
1882         }
1883     }
1884 
1885     void commandSave(final StringTokenizer t) { // Undocumented command: useful for testing.
1886         if (!t.hasMoreTokens()) {
1887             Set<String> keys = Env.getSaveKeys();
1888             if (keys.isEmpty()) {
1889                 MessageOutput.println("No saved values");
1890                 return;
1891             }
1892             for (String key : keys) {
1893                 Value value = Env.getSavedValue(key);
1894                 if ((value instanceof ObjectReference) &&
1895                     ((ObjectReference)value).isCollected()) {
1896                     MessageOutput.println("expr is value <collected>",
1897                                           new Object [] {key, value.toString()});
1898                 } else {
1899                     if (value == null){
1900                         MessageOutput.println("expr is null", key);
1901                     } else {
1902                         MessageOutput.println("expr is value",
1903                                               new Object [] {key, value.toString()});
1904                     }
1905                 }
1906             }
1907         } else {
1908             new AsyncExecution() {
1909                     @Override
1910                     void action() {
1911                         doSave(t);
1912                     }
1913                 };
1914         }
1915 
1916     }
1917 
1918    void commandBytecodes(final StringTokenizer t) { // Undocumented command: useful for testing.
1919         if (!t.hasMoreTokens()) {
1920             MessageOutput.println("No class specified.");
1921             return;
1922         }
1923         String className = t.nextToken();
1924 
1925         if (!t.hasMoreTokens()) {
1926             MessageOutput.println("No method specified.");
1927             return;
1928         }
1929         // Overloading is not handled here.
1930         String methodName = t.nextToken();
1931 
1932         List<ReferenceType> classes = Env.vm().classesByName(className);
1933         // TO DO: handle multiple classes found
1934         if (classes.size() == 0) {
1935             if (className.indexOf('.') < 0) {
1936                 MessageOutput.println("not found (try the full name)", className);
1937             } else {
1938                 MessageOutput.println("not found", className);
1939             }
1940             return;
1941         }
1942 
1943         ReferenceType rt = classes.get(0);
1944         if (!(rt instanceof ClassType)) {
1945             MessageOutput.println("not a class", className);
1946             return;
1947         }
1948 
1949         byte[] bytecodes = null;
1950         for (Method method : rt.methodsByName(methodName)) {
1951             if (!method.isAbstract()) {
1952                 bytecodes = method.bytecodes();
1953                 break;
1954             }
1955         }
1956 
1957         StringBuilder line = new StringBuilder(80);
1958         line.append("0000: ");
1959         for (int i = 0; i < bytecodes.length; i++) {
1960             if ((i > 0) && (i % 16 == 0)) {
1961                 MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
1962                 line.setLength(0);
1963                 line.append(String.valueOf(i));
1964                 line.append(": ");
1965                 int len = line.length();
1966                 for (int j = 0; j < 6 - len; j++) {
1967                     line.insert(0, '0');
1968                 }
1969             }
1970             int val = 0xff & bytecodes[i];
1971             String str = Integer.toHexString(val);
1972             if (str.length() == 1) {
1973                 line.append('0');
1974             }
1975             line.append(str);
1976             line.append(' ');
1977         }
1978         if (line.length() > 6) {
1979             MessageOutput.printDirectln(line.toString());// Special case: use printDirectln()
1980         }
1981     }
1982 
1983     void commandExclude(StringTokenizer t) {
1984         if (!t.hasMoreTokens()) {
1985             MessageOutput.printDirectln(Env.excludesString());// Special case: use printDirectln()
1986         } else {
1987             String rest = t.nextToken("");
1988             if (rest.equals("none")) {
1989                 rest = "";
1990             }
1991             Env.setExcludes(rest);
1992         }
1993     }
1994 
1995     void commandRedefine(StringTokenizer t) {
1996         if (!t.hasMoreTokens()) {
1997             MessageOutput.println("Specify classes to redefine");
1998         } else {
1999             String className = t.nextToken();
2000             List<ReferenceType> classes = Env.vm().classesByName(className);
2001             if (classes.size() == 0) {
2002                 MessageOutput.println("No class named", className);
2003                 return;
2004             }
2005             if (classes.size() > 1) {
2006                 MessageOutput.println("More than one class named", className);
2007                 return;
2008             }
2009             Env.setSourcePath(Env.getSourcePath());
2010             ReferenceType refType = classes.get(0);
2011             if (!t.hasMoreTokens()) {
2012                 MessageOutput.println("Specify file name for class", className);
2013                 return;
2014             }
2015             String fileName = t.nextToken();
2016             File phyl = new File(fileName);
2017             byte[] bytes = new byte[(int)phyl.length()];
2018             try {
2019                 InputStream in = new FileInputStream(phyl);
2020                 in.read(bytes);
2021                 in.close();
2022             } catch (Exception exc) {
2023                 MessageOutput.println("Error reading file",
2024                              new Object [] {fileName, exc.toString()});
2025                 return;
2026             }
2027             Map<ReferenceType, byte[]> map
2028                 = new HashMap<ReferenceType, byte[]>();
2029             map.put(refType, bytes);
2030             try {
2031                 Env.vm().redefineClasses(map);
2032             } catch (Throwable exc) {
2033                 MessageOutput.println("Error redefining class to file",
2034                              new Object [] {className,
2035                                             fileName,
2036                                             exc});
2037             }
2038         }
2039     }
2040 
2041     void commandPopFrames(StringTokenizer t, boolean reenter) {
2042         ThreadInfo threadInfo;
2043 
2044         if (t.hasMoreTokens()) {
2045             String token = t.nextToken();
2046             threadInfo = doGetThread(token);
2047             if (threadInfo == null) {
2048                 return;
2049             }
2050         } else {
2051             threadInfo = ThreadInfo.getCurrentThreadInfo();
2052             if (threadInfo == null) {
2053                 MessageOutput.println("No thread specified.");
2054                 return;
2055             }
2056         }
2057 
2058         try {
2059             StackFrame frame = threadInfo.getCurrentFrame();
2060             threadInfo.getThread().popFrames(frame);
2061             threadInfo = ThreadInfo.getCurrentThreadInfo();
2062             ThreadInfo.setCurrentThreadInfo(threadInfo);
2063             if (reenter) {
2064                 commandStepi();
2065             }
2066         } catch (Throwable exc) {
2067             MessageOutput.println("Error popping frame", exc.toString());
2068         }
2069     }
2070 
2071     void commandExtension(StringTokenizer t) {
2072         if (!t.hasMoreTokens()) {
2073             MessageOutput.println("No class specified.");
2074             return;
2075         }
2076 
2077         String idClass = t.nextToken();
2078         ReferenceType cls = Env.getReferenceTypeFromToken(idClass);
2079         String extension = null;
2080         if (cls != null) {
2081             try {
2082                 extension = cls.sourceDebugExtension();
2083                 MessageOutput.println("sourcedebugextension", extension);
2084             } catch (AbsentInformationException e) {
2085                 MessageOutput.println("No sourcedebugextension specified");
2086             }
2087         } else {
2088             MessageOutput.println("is not a valid id or class name", idClass);
2089         }
2090     }
2091 
2092     void commandVersion(String debuggerName,
2093                         VirtualMachineManager vmm) {
2094         MessageOutput.println("minus version",
2095                               new Object [] { debuggerName,
2096                                               new Integer(vmm.majorInterfaceVersion()),
2097                                               new Integer(vmm.minorInterfaceVersion()),
2098                                                   System.getProperty("java.version")});
2099         if (Env.connection() != null) {
2100             try {
2101                 MessageOutput.printDirectln(Env.vm().description());// Special case: use printDirectln()
2102             } catch (VMNotConnectedException e) {
2103                 MessageOutput.println("No VM connected");
2104             }
2105         }
2106     }
2107 }