1 /*
   2  * Copyright (c) 2002, 2017, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.ui.classbrowser;
  26 
  27 import java.io.*;
  28 import java.util.*;
  29 import sun.jvm.hotspot.asm.*;
  30 import sun.jvm.hotspot.code.*;
  31 import sun.jvm.hotspot.compiler.*;
  32 import sun.jvm.hotspot.debugger.*;
  33 import sun.jvm.hotspot.interpreter.*;
  34 import sun.jvm.hotspot.oops.*;
  35 import sun.jvm.hotspot.runtime.*;
  36 import sun.jvm.hotspot.tools.jcore.*;
  37 import sun.jvm.hotspot.utilities.*;
  38 
  39 public class HTMLGenerator implements /* imports */ ClassConstants {
  40     static class Formatter {
  41         boolean html;
  42         StringBuffer buf = new StringBuffer();
  43 
  44         Formatter(boolean h) {
  45             html = h;
  46         }
  47 
  48         void append(String s) {
  49             buf.append(s);
  50         }
  51 
  52         void append(int s) {
  53             buf.append(s);
  54         }
  55 
  56         void append(char s) {
  57             buf.append(s);
  58         }
  59 
  60         void append(StringBuffer s) {
  61             buf.append(s);
  62         }
  63 
  64         void append(Formatter s) {
  65             buf.append(s);
  66         }
  67 
  68         StringBuffer getBuffer() {
  69             return buf;
  70         }
  71 
  72         public String toString() {
  73             return buf.toString();
  74         }
  75 
  76         void wrap(String tag, String text) {
  77             wrap(tag, tag, text);
  78         }
  79         void wrap(String before, String after, String text) {
  80             beginTag(before);
  81             append(text);
  82             endTag(after);
  83         }
  84 
  85         // header tags
  86         void h1(String s) { nl(); wrap("h1", s); nl(); }
  87         void h2(String s) { nl(); wrap("h2", s); nl(); }
  88         void h3(String s) { nl(); wrap("h3", s); nl(); }
  89         void h4(String s) { nl(); wrap("h4", s); nl(); }
  90 
  91         // list tags
  92         void beginList()  { beginTag("ul"); nl(); }
  93         void endList()    { endTag("ul"); nl();   }
  94         void beginListItem() { beginTag("li"); }
  95         void endListItem()   { endTag("li"); nl();   }
  96         void li(String s) { wrap("li", s); nl();  }
  97 
  98         // table tags
  99         void beginTable(int border) {
 100             beginTag("table border='" + border + "'");
 101         }
 102         void cell(String s) { wrap("td", s); }
 103         void headerCell(String s) { wrap("th", s); }
 104         void endTable() { endTag("table"); }
 105 
 106         void link(String href, String text) {
 107             wrap("a href='" + href + "'", "a", text);
 108         }
 109         void beginTag(String s) {
 110             if (html) { append("<"); append(s); append(">"); }
 111         }
 112         void endTag(String s) {
 113             if (html) {
 114                append("</"); append(s); append(">");
 115             } else {
 116                if (s.equals("table") || s.equals("tr")) {
 117                   nl();
 118                }
 119                if (s.equals("td") || s.equals("th")) {
 120                   append(" ");
 121                }
 122             }
 123         }
 124         void bold(String s) {
 125             wrap("b", s);
 126         }
 127 
 128         void nl() {
 129             if (!html) buf.append("\n");
 130         }
 131 
 132         void br() {
 133             if (html) append("<br>");
 134             else      append("\n");
 135         }
 136         void genEmptyHTML() {
 137             if (html) append("<html></html>");
 138         }
 139 
 140         void genHTMLPrologue() {
 141             if (html) append("<html><body>");
 142         }
 143 
 144         void genHTMLPrologue(String title) {
 145             if (html) {
 146                 append("<html><head><title>");
 147                 append(title);
 148                 append("</title></head>");
 149                 append("<body>");
 150             }
 151             h2(title);
 152         }
 153         void genHTMLEpilogue() {
 154             if (html) append("</body></html>");
 155         }
 156 
 157     }
 158 
 159    private static final String DUMP_KLASS_OUTPUT_DIR = ".";
 160    private static final int NATIVE_CODE_SIZE = 200;
 161    private final String spaces;
 162    private final String tab;
 163 
 164    private boolean genHTML = true;
 165 
 166    public HTMLGenerator() {
 167        this(true);
 168    }
 169 
 170    public HTMLGenerator(boolean html) {
 171        genHTML = html;
 172        if (html) {
 173            spaces = "&nbsp;&nbsp;";
 174            tab = "&nbsp;&nbsp;&nbsp;&nbsp;";
 175        } else {
 176            spaces = "  ";
 177            tab = "    ";
 178        }
 179    }
 180 
 181    protected String escapeHTMLSpecialChars(String value) {
 182       if (!genHTML) return value;
 183 
 184       Formatter buf = new Formatter(genHTML);
 185       int len = value.length();
 186       for (int i=0; i < len; i++) {
 187          char c = value.charAt(i);
 188          switch (c) {
 189             case '<':
 190                buf.append("&lt;");
 191                break;
 192             case '>':
 193                buf.append("&gt;");
 194                break;
 195             case '&':
 196                buf.append("&amp;");
 197                break;
 198             default:
 199                buf.append(c);
 200                break;
 201          }
 202       }
 203       return buf.toString();
 204    }
 205 
 206    public String genHTMLForMessage(String message) {
 207       Formatter buf = new Formatter(genHTML);
 208       buf.genHTMLPrologue(message);
 209       buf.genHTMLEpilogue();
 210       return buf.toString();
 211    }
 212 
 213    public String genHTMLErrorMessage(Exception exp) {
 214       exp.printStackTrace();
 215       return genHTMLForMessage(exp.getClass().getName() + " : " + exp.getMessage());
 216    }
 217 
 218    public String genHTMLForWait(String message) {
 219       Formatter buf = new Formatter(genHTML);
 220       buf.genHTMLPrologue("Please wait ..");
 221       buf.h2(message);
 222       return buf.toString();
 223    }
 224 
 225    protected String genKlassTitle(InstanceKlass klass) {
 226       Formatter buf = new Formatter(genHTML);
 227       AccessFlags acc = klass.getAccessFlagsObj();
 228       if (acc.isPublic()) {
 229          buf.append("public ");
 230       } else if (acc.isProtected()) {
 231          buf.append("protected ");
 232       } else if (acc.isPrivate()) {
 233          buf.append("private ");
 234       }
 235 
 236       if (acc.isStatic()) {
 237          buf.append("static ");
 238       }
 239 
 240       if (acc.isAbstract() ) {
 241          buf.append("abstract ");
 242       } else if (acc.isFinal()) {
 243          buf.append("final ");
 244       }
 245 
 246       if (acc.isStrict()) {
 247          buf.append("strict ");
 248       }
 249 
 250       // javac generated flags
 251       if (acc.isEnum()) {
 252          buf.append("[enum] ");
 253       }
 254       if (acc.isSynthetic()) {
 255          buf.append("[synthetic] ");
 256       }
 257 
 258       if (klass.isInterface()) {
 259          buf.append("interface");
 260       } else {
 261          buf.append("class");
 262       }
 263 
 264       buf.append(' ');
 265       buf.append(klass.getName().asString().replace('/', '.'));
 266       // is it generic?
 267       Symbol genSig = klass.getGenericSignature();
 268       if (genSig != null) {
 269          buf.append(" [signature ");
 270          buf.append(escapeHTMLSpecialChars(genSig.asString()));
 271          buf.append("] ");
 272       } else {
 273          buf.append(' ');
 274       }
 275       buf.append('@');
 276       buf.append(klass.getAddress().toString());
 277       return buf.toString();
 278    }
 279 
 280    protected String genBaseHref() {
 281       return "";
 282    }
 283 
 284    protected String genKlassHref(InstanceKlass klass) {
 285       return genBaseHref() + "klass=" + klass.getAddress();
 286    }
 287 
 288    protected String genKlassLink(InstanceKlass klass) {
 289       Formatter buf = new Formatter(genHTML);
 290       buf.link(genKlassHref(klass), genKlassTitle(klass));
 291       return buf.toString();
 292    }
 293 
 294    protected String genMethodModifierString(AccessFlags acc) {
 295       Formatter buf = new Formatter(genHTML);
 296       if (acc.isPrivate()) {
 297          buf.append("private ");
 298       } else if (acc.isProtected()) {
 299          buf.append("protected ");
 300       } else if (acc.isPublic()) {
 301          buf.append("public ");
 302       }
 303 
 304       if (acc.isStatic()) {
 305          buf.append("static ");
 306       } else if (acc.isAbstract() ) {
 307          buf.append("abstract ");
 308       } else if (acc.isFinal()) {
 309          buf.append("final ");
 310       }
 311 
 312        if (acc.isNative()) {
 313          buf.append("native ");
 314       }
 315 
 316       if (acc.isStrict()) {
 317          buf.append("strict ");
 318       }
 319 
 320       if (acc.isSynchronized()) {
 321          buf.append("synchronized ");
 322       }
 323 
 324       // javac generated flags
 325       if (acc.isBridge()) {
 326          buf.append("[bridge] ");
 327       }
 328 
 329       if (acc.isSynthetic()) {
 330          buf.append("[synthetic] ");
 331       }
 332 
 333       if (acc.isVarArgs()) {
 334          buf.append("[varargs] ");
 335       }
 336 
 337       return buf.toString();
 338    }
 339 
 340    protected String genMethodNameAndSignature(Method method) {
 341       Formatter buf = new Formatter(genHTML);
 342       buf.append(genMethodModifierString(method.getAccessFlagsObj()));
 343       Symbol sig = method.getSignature();
 344       new SignatureConverter(sig, buf.getBuffer()).iterateReturntype();
 345       buf.append(" ");
 346       String methodName = method.getName().asString();
 347       buf.append(escapeHTMLSpecialChars(methodName));
 348       buf.append('(');
 349       new SignatureConverter(sig, buf.getBuffer()).iterateParameters();
 350       buf.append(')');
 351       // is it generic?
 352       Symbol genSig = method.getGenericSignature();
 353       if (genSig != null) {
 354          buf.append(" [signature ");
 355          buf.append(escapeHTMLSpecialChars(genSig.asString()));
 356          buf.append("] ");
 357       }
 358       return buf.toString().replace('/', '.');
 359    }
 360 
 361    protected String genMethodTitle(Method method) {
 362       Formatter buf = new Formatter(genHTML);
 363       buf.append(genMethodNameAndSignature(method));
 364       buf.append(' ');
 365       buf.append('@');
 366       buf.append(method.getAddress().toString());
 367       return buf.toString();
 368    }
 369 
 370    protected String genMethodHref(Method m) {
 371       return genBaseHref() + "method=" + m.getAddress();
 372    }
 373 
 374    protected String genMethodLink(Method m) {
 375       Formatter buf = new Formatter(genHTML);
 376       buf.link(genMethodHref(m), genMethodTitle(m));
 377       return buf.toString();
 378    }
 379 
 380    protected String genMethodAndKlassLink(Method m) {
 381       Formatter buf = new Formatter(genHTML);
 382       buf.append(genMethodLink(m));
 383       buf.append(" of ");
 384       buf.append(genKlassLink((InstanceKlass) m.getMethodHolder()));
 385       return buf.toString();
 386    }
 387 
 388    protected String genNMethodHref(NMethod nm) {
 389       return genBaseHref() + "nmethod=" + nm.getAddress();
 390    }
 391 
 392    public String genNMethodTitle(NMethod nmethod) {
 393       Formatter buf = new Formatter(genHTML);
 394       Method m = nmethod.getMethod();
 395 
 396       buf.append("Disassembly for compiled method [");
 397       buf.append(genMethodTitle(m));
 398       buf.append(" ] ");
 399       buf.append('@');
 400       buf.append(nmethod.getAddress().toString());
 401       return buf.toString();
 402    }
 403 
 404    protected String genNMethodLink(NMethod nm) {
 405       Formatter buf = new Formatter(genHTML);
 406       buf.link(genNMethodHref(nm), genNMethodTitle(nm));
 407       return buf.toString();
 408    }
 409 
 410    public String genCodeBlobTitle(CodeBlob blob) {
 411       Formatter buf = new Formatter(genHTML);
 412       buf.append("Disassembly for code blob " + blob.getName() + " [");
 413       buf.append(blob.getClass().getName());
 414       buf.append(" ] @");
 415       buf.append(blob.getAddress().toString());
 416       return buf.toString();
 417    }
 418 
 419    protected BytecodeDisassembler createBytecodeDisassembler(Method m) {
 420       return new BytecodeDisassembler(m);
 421    }
 422 
 423    private String genLowHighShort(int val) {
 424       Formatter buf = new Formatter(genHTML);
 425       buf.append('#');
 426       buf.append(Integer.toString(val & 0xFFFF));
 427       buf.append(" #");
 428       buf.append(Integer.toString((val >> 16) & 0xFFFF));
 429       return buf.toString();
 430    }
 431 
 432    private String genListOfShort(short[] values) {
 433       if (values == null || values.length == 0)  return "";
 434       Formatter buf = new Formatter(genHTML);
 435       buf.append('[');
 436       for (int i = 0; i < values.length; i++) {
 437           if (i > 0)  buf.append(' ');
 438           buf.append('#');
 439           buf.append(Integer.toString(values[i]));
 440       }
 441       buf.append(']');
 442       return buf.toString();
 443    }
 444 
 445    protected String genHTMLTableForConstantPool(ConstantPool cpool) {
 446       Formatter buf = new Formatter(genHTML);
 447       buf.beginTable(1);
 448 
 449       buf.beginTag("tr");
 450       buf.headerCell("Index");
 451       buf.headerCell("Constant Type");
 452       buf.headerCell("Constant Value");
 453       buf.endTag("tr");
 454 
 455       final int length = (int) cpool.getLength();
 456       // zero'th pool entry is always invalid. ignore it.
 457       for (int index = 1; index < length; index++) {
 458          buf.beginTag("tr");
 459          buf.cell(Integer.toString(index));
 460 
 461          int ctag = (int) cpool.getTags().at((int) index);
 462          switch (ctag) {
 463             case JVM_CONSTANT_Integer:
 464                buf.cell("JVM_CONSTANT_Integer");
 465                buf.cell(Integer.toString(cpool.getIntAt(index)));
 466                break;
 467 
 468             case JVM_CONSTANT_Float:
 469                buf.cell("JVM_CONSTANT_Float");
 470                buf.cell(Float.toString(cpool.getFloatAt(index)));
 471                break;
 472 
 473             case JVM_CONSTANT_Long:
 474                buf.cell("JVM_CONSTANT_Long");
 475                buf.cell(Long.toString(cpool.getLongAt(index)));
 476                // long entries occupy two slots
 477                index++;
 478                break;
 479 
 480             case JVM_CONSTANT_Double:
 481                buf.cell("JVM_CONSTANT_Double");
 482                buf.cell(Double.toString(cpool.getDoubleAt(index)));
 483                // double entries occupy two slots
 484                index++;
 485                break;
 486 
 487             case JVM_CONSTANT_UnresolvedClass:
 488                buf.cell("JVM_CONSTANT_UnresolvedClass");
 489                buf.cell(cpool.getKlassNameAt(index).asString());
 490                break;
 491 
 492             case JVM_CONSTANT_UnresolvedClassInError:
 493                buf.cell("JVM_CONSTANT_UnresolvedClassInError");
 494                buf.cell(cpool.getSymbolAt(index).asString());
 495                break;
 496 
 497             case JVM_CONSTANT_Class:
 498                buf.cell("JVM_CONSTANT_Class");
 499                Klass klass = (Klass) cpool.getKlassAt(index);
 500                if (klass instanceof InstanceKlass) {
 501                   buf.cell(genKlassLink((InstanceKlass) klass));
 502                } else {
 503                   buf.cell(klass.getName().asString().replace('/', '.'));
 504                }
 505                break;
 506 
 507             case JVM_CONSTANT_Utf8:
 508                buf.cell("JVM_CONSTANT_Utf8");
 509                buf.cell("\"" +
 510                  escapeHTMLSpecialChars(cpool.getSymbolAt(index).asString()) +
 511                  "\"");
 512                break;
 513 
 514             case JVM_CONSTANT_String:
 515                buf.cell("JVM_CONSTANT_String");
 516                buf.cell("\"" +
 517                         escapeHTMLSpecialChars(cpool.getUnresolvedStringAt(index).asString()) + "\"");
 518                break;
 519 
 520             case JVM_CONSTANT_Fieldref:
 521                buf.cell("JVM_CONSTANT_Fieldref");
 522                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 523                break;
 524 
 525             case JVM_CONSTANT_Methodref:
 526                buf.cell("JVM_CONSTANT_Methodref");
 527                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 528                break;
 529 
 530             case JVM_CONSTANT_InterfaceMethodref:
 531                buf.cell("JVM_CONSTANT_InterfaceMethodref");
 532                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 533                break;
 534 
 535             case JVM_CONSTANT_NameAndType:
 536                buf.cell("JVM_CONSTANT_NameAndType");
 537                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 538                break;
 539 
 540             case JVM_CONSTANT_ClassIndex:
 541                buf.cell("JVM_CONSTANT_ClassIndex");
 542                buf.cell(Integer.toString(cpool.getIntAt(index)));
 543                break;
 544 
 545             case JVM_CONSTANT_StringIndex:
 546                buf.cell("JVM_CONSTANT_StringIndex");
 547                buf.cell(Integer.toString(cpool.getIntAt(index)));
 548                break;
 549 
 550             case JVM_CONSTANT_MethodHandle:
 551                buf.cell("JVM_CONSTANT_MethodHandle");
 552                buf.cell(genLowHighShort(cpool.getIntAt(index)));
 553                break;
 554 
 555             case JVM_CONSTANT_MethodType:
 556                buf.cell("JVM_CONSTANT_MethodType");
 557                buf.cell(Integer.toString(cpool.getIntAt(index)));
 558                break;
 559 
 560             case JVM_CONSTANT_InvokeDynamic:
 561                buf.cell("JVM_CONSTANT_InvokeDynamic");
 562                buf.cell(genLowHighShort(cpool.getIntAt(index)) +
 563                         genListOfShort(cpool.getBootstrapSpecifierAt(index)));
 564                break;
 565 
 566             default:
 567                throw new InternalError("unknown tag: " + ctag);
 568          }
 569 
 570          buf.endTag("tr");
 571       }
 572 
 573       buf.endTable();
 574       return buf.toString();
 575    }
 576 
 577    public String genHTML(ConstantPool cpool) {
 578       try {
 579          Formatter buf = new Formatter(genHTML);
 580          buf.genHTMLPrologue(genConstantPoolTitle(cpool));
 581          buf.h3("Holder Class");
 582          buf.append(genKlassLink((InstanceKlass) cpool.getPoolHolder()));
 583          buf.h3("Constants");
 584          buf.append(genHTMLTableForConstantPool(cpool));
 585          buf.genHTMLEpilogue();
 586          return buf.toString();
 587       } catch (Exception exp) {
 588          return genHTMLErrorMessage(exp);
 589       }
 590    }
 591 
 592    protected String genConstantPoolHref(ConstantPool cpool) {
 593       return genBaseHref() + "cpool=" + cpool.getAddress();
 594    }
 595 
 596    protected String genConstantPoolTitle(ConstantPool cpool) {
 597       Formatter buf = new Formatter(genHTML);
 598       buf.append("Constant Pool of [");
 599       buf.append(genKlassTitle((InstanceKlass) cpool.getPoolHolder()));
 600       buf.append("] @");
 601       buf.append(cpool.getAddress().toString());
 602       return buf.toString();
 603    }
 604 
 605    protected String genConstantPoolLink(ConstantPool cpool) {
 606       Formatter buf = new Formatter(genHTML);
 607       buf.link(genConstantPoolHref(cpool), genConstantPoolTitle(cpool));
 608       return buf.toString();
 609    }
 610 
 611    public String genHTML(Method method) {
 612       try {
 613          final Formatter buf = new Formatter(genHTML);
 614          buf.genHTMLPrologue(genMethodTitle(method));
 615 
 616          buf.h3("Holder Class");
 617          buf.append(genKlassLink((InstanceKlass) method.getMethodHolder()));
 618 
 619          NMethod nmethod = method.getNativeMethod();
 620          if (nmethod != null) {
 621             buf.h3("Compiled Code");
 622             buf.append(genNMethodLink(nmethod));
 623          }
 624 
 625          boolean hasThrows = method.hasCheckedExceptions();
 626          ConstantPool cpool = ((InstanceKlass) method.getMethodHolder()).getConstants();
 627          if (hasThrows) {
 628             buf.h3("Checked Exception(s)");
 629             CheckedExceptionElement[] exceptions = method.getCheckedExceptions();
 630             buf.beginTag("ul");
 631             for (int exp = 0; exp < exceptions.length; exp++) {
 632                short cpIndex = (short) exceptions[exp].getClassCPIndex();
 633                ConstantTag tag = cpool.getTagAt(cpIndex);
 634                if (tag.isUnresolvedKlass()) {
 635                  buf.li(cpool.getKlassNameAt(cpIndex).asString().replace('/', '.'));
 636                } else {
 637                  Klass k = cpool.getKlassAt(cpIndex);
 638                  buf.li(genKlassLink((InstanceKlass)k));
 639                }
 640             }
 641             buf.endTag("ul");
 642          }
 643 
 644          if (method.isNative() || method.isAbstract()) {
 645            buf.genHTMLEpilogue();
 646            return buf.toString();
 647          }
 648 
 649          buf.h3("Bytecode");
 650          BytecodeDisassembler disasm = createBytecodeDisassembler(method);
 651          final boolean hasLineNumbers = method.hasLineNumberTable();
 652          disasm.decode(new BytecodeVisitor() {
 653                           private Method method;
 654                           public void prologue(Method m) {
 655                              method = m;
 656                              buf.beginTable(0);
 657                              buf.beginTag("tr");
 658                              if (hasLineNumbers) {
 659                                 buf.headerCell("line");
 660                              }
 661                              buf.headerCell("bci" + spaces);
 662                              buf.headerCell("bytecode");
 663                              buf.endTag("tr");
 664                           }
 665 
 666                           public void visit(Bytecode instr) {
 667                              int curBci = instr.bci();
 668                              buf.beginTag("tr");
 669                              if (hasLineNumbers) {
 670                                 int lineNumber = method.getLineNumberFromBCI(curBci);
 671                                 buf.cell(Integer.toString(lineNumber) + spaces);
 672                              }
 673                              buf.cell(Integer.toString(curBci) + spaces);
 674 
 675                              buf.beginTag("td");
 676                              String instrStr = null;
 677                              try {
 678                                  instrStr = escapeHTMLSpecialChars(instr.toString());
 679                              } catch (RuntimeException re) {
 680                                  buf.append("exception while printing " + instr.getBytecodeName());
 681                                  buf.endTag("td");
 682                                  buf.endTag("tr");
 683                                  re.printStackTrace();
 684                                  return;
 685                              }
 686 
 687                              if (instr instanceof BytecodeNew) {
 688                                 BytecodeNew newBytecode = (BytecodeNew) instr;
 689                                 InstanceKlass klass = newBytecode.getNewKlass();
 690                                 if (klass != null) {
 691                                     buf.link(genKlassHref(klass), instrStr);
 692                                 } else {
 693                                     buf.append(instrStr);
 694                                 }
 695                              } else if (instr instanceof BytecodeInvoke) {
 696                                 BytecodeInvoke invokeBytecode = (BytecodeInvoke) instr;
 697                                 if (invokeBytecode.isInvokedynamic()) {
 698                                   buf.append(instrStr);
 699                                 } else {
 700                                   Method m = invokeBytecode.getInvokedMethod();
 701                                   if (m != null) {
 702                                     buf.link(genMethodHref(m), instrStr);
 703                                     buf.append(" of ");
 704                                     InstanceKlass klass = (InstanceKlass) m.getMethodHolder();
 705                                     buf.link(genKlassHref(klass), genKlassTitle(klass));
 706                                   } else {
 707                                     buf.append(instrStr);
 708                                   }
 709                                }
 710                              } else if (instr instanceof BytecodeGetPut) {
 711                                 BytecodeGetPut getPut = (BytecodeGetPut) instr;
 712                                 sun.jvm.hotspot.oops.Field f = getPut.getField();
 713                                 buf.append(instrStr);
 714                                 if (f != null) {
 715                                    InstanceKlass klass = f.getFieldHolder();
 716                                    buf.append(" of ");
 717                                    buf.link(genKlassHref(klass), genKlassTitle(klass));
 718                                 }
 719                              } else if (instr instanceof BytecodeLoadConstant) {
 720                                 BytecodeLoadConstant ldc = (BytecodeLoadConstant) instr;
 721                                 if (ldc.isKlassConstant()) {
 722                                    Object oop = ldc.getKlass();
 723                                    if (oop instanceof InstanceKlass) {
 724                                       buf.append("<a href='");
 725                                       buf.append(genKlassHref((InstanceKlass) oop));
 726                                       buf.append("'>");
 727                                       buf.append(instrStr);
 728                                       buf.append("</a>");
 729                                    } else {
 730                                       // unresolved klass literal
 731                                       buf.append(instrStr);
 732                                    }
 733                                 } else {
 734                                    // not a klass literal
 735                                    buf.append(instrStr);
 736                                 }
 737                              } else {
 738                                 buf.append(instrStr);
 739                              }
 740                              buf.endTag("td");
 741                              buf.endTag("tr");
 742                           }
 743 
 744                           public void epilogue() {
 745                              buf.endTable();
 746                           }
 747                        });
 748 
 749          // display exception table for this method
 750          boolean hasException = method.hasExceptionTable();
 751          if (hasException) {
 752             ExceptionTableElement[] exceptionTable = method.getExceptionTable();
 753             int numEntries = exceptionTable.length;
 754             if (numEntries != 0) {
 755                buf.h4("Exception Table");
 756                buf.beginTable(1);
 757                buf.beginTag("tr");
 758                buf.headerCell("start bci");
 759                buf.headerCell("end bci");
 760                buf.headerCell("handler bci");
 761                buf.headerCell("catch type");
 762                buf.endTag("tr");
 763 
 764                for (int e = 0; e < numEntries; e ++) {
 765                   buf.beginTag("tr");
 766                   buf.cell(Integer.toString(exceptionTable[e].getStartPC()));
 767                   buf.cell(Integer.toString(exceptionTable[e].getEndPC()));
 768                   buf.cell(Integer.toString(exceptionTable[e].getHandlerPC()));
 769                   short cpIndex = (short) exceptionTable[e].getCatchTypeIndex();
 770                   ConstantTag tag = cpIndex == 0? null : cpool.getTagAt(cpIndex);
 771                   if (tag == null) {
 772                     buf.cell("Any");
 773                   } else if (tag.isUnresolvedKlass()) {
 774                     buf.cell(cpool.getKlassNameAt(cpIndex).asString().replace('/', '.'));
 775                   } else {
 776                     Klass k = cpool.getKlassAt(cpIndex);
 777                     buf.cell(genKlassLink((InstanceKlass)k));
 778                   }
 779                   buf.endTag("tr");
 780                }
 781 
 782                buf.endTable();
 783             }
 784          }
 785 
 786          // display constant pool hyperlink
 787          buf.h3("Constant Pool");
 788          buf.append(genConstantPoolLink(cpool));
 789          buf.genHTMLEpilogue();
 790          return buf.toString();
 791       } catch (Exception exp) {
 792          return genHTMLErrorMessage(exp);
 793       }
 794    }
 795 
 796    protected SymbolFinder createSymbolFinder() {
 797       return new DummySymbolFinder();
 798    }
 799 
 800    // genHTML for a given address. Address may be a PC or
 801    // Method* or Klass*.
 802 
 803    public String genHTMLForAddress(String addrStr) {
 804       return genHTML(parseAddress(addrStr));
 805    }
 806 
 807    public String genHTML(sun.jvm.hotspot.debugger.Address pc) {
 808       CodeBlob blob = null;
 809 
 810       try {
 811          blob = (CodeBlob)VM.getVM().getCodeCache().findBlobUnsafe(pc);
 812       } catch (Exception exp) {
 813          // ignore
 814       }
 815 
 816       if (blob != null) {
 817          if (blob instanceof NMethod) {
 818             return genHTML((NMethod)blob);
 819          } else {
 820             // may be interpreter code.
 821             Interpreter interp = VM.getVM().getInterpreter();
 822             if (interp.contains(pc)) {
 823                InterpreterCodelet codelet = interp.getCodeletContaining(pc);
 824                if (codelet == null) {
 825                   return "Unknown location in the Interpreter: " + pc;
 826                }
 827                return genHTML(codelet);
 828             }
 829             return genHTML(blob);
 830          }
 831       } else if (VM.getVM().getCodeCache().contains(pc)) {
 832          return "Unknown location in the CodeCache: " + pc;
 833       }
 834 
 835       // did not find nmethod.
 836       // try Method*, Klass* and ConstantPool*.
 837       try {
 838         Metadata obj = Metadata.instantiateWrapperFor(pc);
 839          if (obj != null) {
 840             if (obj instanceof Method) {
 841                return genHTML((Method) obj);
 842             } else if (obj instanceof InstanceKlass) {
 843                return genHTML((InstanceKlass) obj);
 844             } else if (obj instanceof ConstantPool) {
 845                return genHTML((ConstantPool) obj);
 846             }
 847          }
 848       } catch (Exception exp) {
 849         exp.printStackTrace();
 850          // ignore
 851       }
 852 
 853       // didn't find any. do raw disassembly.
 854       return genHTMLForRawDisassembly(pc, null);
 855    }
 856 
 857    public String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc, int size) {
 858       try {
 859          return genHTMLForRawDisassembly(startPc, size, null);
 860       } catch (Exception exp) {
 861          return genHTMLErrorMessage(exp);
 862       }
 863    }
 864 
 865    protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address startPc,
 866                                              String prevPCs) {
 867       try {
 868          return genHTMLForRawDisassembly(startPc, NATIVE_CODE_SIZE, prevPCs);
 869       } catch (Exception exp) {
 870          return genHTMLErrorMessage(exp);
 871       }
 872    }
 873 
 874    protected String genPCHref(long targetPc) {
 875       return genBaseHref() + "pc=0x" + Long.toHexString(targetPc);
 876    }
 877 
 878    protected String genMultPCHref(String pcs) {
 879       StringBuffer buf = new StringBuffer(genBaseHref());
 880       buf.append("pc_multiple=");
 881       buf.append(pcs);
 882       return buf.toString();
 883    }
 884 
 885    protected String genPCHref(Address addr) {
 886       return genPCHref(addressToLong(addr));
 887    }
 888 
 889    class HTMLDisassembler implements InstructionVisitor {
 890       private int instrSize = 0;
 891       private Formatter buf;
 892       private SymbolFinder symFinder = createSymbolFinder();
 893       private long pc;
 894       private ImmutableOopMapSet oms;
 895       private CodeBlob blob;
 896       private NMethod nmethod;
 897 
 898       HTMLDisassembler(Formatter buf, CodeBlob blob) {
 899          this.buf = buf;
 900          this.blob = blob;
 901          if (blob != null) {
 902             if (blob instanceof NMethod) {
 903                nmethod = (NMethod)blob;
 904             }
 905             oms = blob.getOopMaps();
 906          }
 907       }
 908 
 909       public int getInstructionSize() {
 910          return  instrSize;
 911       }
 912 
 913       public void prologue() {
 914       }
 915 
 916       public void beginInstruction(long currentPc) {
 917          pc = currentPc;
 918 
 919          sun.jvm.hotspot.debugger.Address adr = longToAddress(pc);
 920          if (nmethod != null) {
 921             if (adr.equals(nmethod.getEntryPoint()))             print("[Entry Point]\n");
 922             if (adr.equals(nmethod.getVerifiedEntryPoint()))     print("[Verified Entry Point]\n");
 923             if (adr.equals(nmethod.exceptionBegin()))            print("[Exception Handler]\n");
 924             if (adr.equals(nmethod.stubBegin()) &&
 925                 !nmethod.stubBegin().equals(nmethod.stubEnd()))  print("[Stub Code]\n");
 926             // if (adr.equals(nmethod.constsBegin()))               print("[Constants]\n");
 927          }
 928 
 929          buf.append(adr.toString());
 930          buf.append(':');
 931          buf.append(tab);
 932       }
 933 
 934       public void printAddress(long address) {
 935          sun.jvm.hotspot.debugger.Address addr = longToAddress(address);
 936          if (VM.getVM().getCodeCache().contains(addr)) {
 937             buf.link(genPCHref(address), addr.toString());
 938          } else {
 939             buf.append(addr.toString());
 940          }
 941       }
 942 
 943       public void print(String s) {
 944          buf.append(s);
 945       }
 946 
 947       public void endInstruction(long endPc) {
 948          instrSize += endPc - pc;
 949          if (genHTML) buf.br();
 950 
 951          if (nmethod != null) {
 952             ScopeDesc sd = nmethod.scope_desc_in(pc, endPc);
 953             if (sd != null) {
 954                buf.br();
 955                buf.append(genSafepointInfo(nmethod, sd));
 956             }
 957          }
 958 
 959          if (oms != null) {
 960             long base = addressToLong(blob.codeBegin());
 961             for (int i = 0, imax = oms.getCount(); i < imax; i++) {
 962                ImmutableOopMapPair pair = oms.getPairAt(i);
 963                long omspc = base + pair.getPC();
 964                if (omspc > pc) {
 965                   if (omspc <= endPc) {
 966                      buf.br();
 967                      buf.append(genOopMapInfo(oms.getMap(pair)));
 968                      // st.move_to(column);
 969                      // visitor.print("; ");
 970                         // om.print_on(st);
 971                   }
 972                   break;
 973                }
 974             }
 975          }
 976          // follow each complete insn by a nice newline
 977          buf.br();
 978       }
 979 
 980       public void epilogue() {
 981       }
 982    };
 983 
 984    protected String genHTMLForRawDisassembly(sun.jvm.hotspot.debugger.Address addr,
 985                                              int size,
 986                                              String prevPCs) {
 987       try {
 988          final Formatter buf = new Formatter(genHTML);
 989          buf.genHTMLPrologue("Disassembly @ " + addr);
 990 
 991          if (prevPCs != null && genHTML) {
 992              buf.beginTag("p");
 993              buf.link(genMultPCHref(prevPCs), "show previous code ..");
 994              buf.endTag("p");
 995          }
 996 
 997 
 998          buf.h3("Code");
 999          HTMLDisassembler visitor = new HTMLDisassembler(buf, null);
1000          Disassembler.decode(visitor, null, addr, addr.addOffsetTo(size));
1001 
1002          if (genHTML) buf.beginTag("p");
1003          Formatter tmpBuf = new Formatter(genHTML);
1004          long startPc = addressToLong(addr);
1005          tmpBuf.append("0x");
1006          tmpBuf.append(Long.toHexString(startPc + visitor.getInstructionSize()).toString());
1007          tmpBuf.append(",0x");
1008          tmpBuf.append(Long.toHexString(startPc));
1009          if (prevPCs != null) {
1010             tmpBuf.append(',');
1011             tmpBuf.append(prevPCs);
1012          }
1013          if (genHTML) {
1014              buf.link(genMultPCHref(tmpBuf.toString()), "show more code ..");
1015              buf.endTag("p");
1016          }
1017 
1018          buf.genHTMLEpilogue();
1019          return buf.toString();
1020       } catch (Exception exp) {
1021          return genHTMLErrorMessage(exp);
1022       }
1023    }
1024 
1025    protected String genSafepointInfo(NMethod nm, ScopeDesc sd) {
1026        Formatter buf = new Formatter(genHTML);
1027        Formatter tabs = new Formatter(genHTML);
1028        tabs.append(tab + tab + tab); // Initial indent for debug info
1029 
1030        buf.beginTag("pre");
1031        genScope(buf, tabs, sd);
1032 
1033        // Reset indent for scalar replaced objects
1034        tabs = new Formatter(genHTML);
1035        tabs.append(tab + tab + tab); // Initial indent for debug info
1036 
1037        genScObjInfo(buf, tabs, sd);
1038        buf.endTag("pre");
1039 
1040        return buf.toString();
1041    }
1042 
1043     protected void genScope(Formatter buf, Formatter tabs, ScopeDesc sd) {
1044         if (sd == null) {
1045             return;
1046         }
1047 
1048         genScope(buf, tabs, sd.sender());
1049 
1050         buf.append(tabs);
1051         Method m = sd.getMethod();
1052         buf.append(genMethodAndKlassLink(m));
1053         int bci = sd.getBCI();
1054         buf.append(" @ bci = ");
1055         buf.append(Integer.toString(bci));
1056 
1057         int line = m.getLineNumberFromBCI(bci);
1058         if (line != -1) {
1059             buf.append(", line = ");
1060             buf.append(Integer.toString(line));
1061         }
1062 
1063         List locals = sd.getLocals();
1064         if (locals != null) {
1065             buf.br();
1066             buf.append(tabs);
1067             buf.append(genHTMLForLocals(sd, locals));
1068         }
1069 
1070         List expressions = sd.getExpressions();
1071         if (expressions != null) {
1072             buf.br();
1073             buf.append(tabs);
1074             buf.append(genHTMLForExpressions(sd, expressions));
1075         }
1076 
1077         List monitors = sd.getMonitors();
1078         if (monitors != null) {
1079             buf.br();
1080             buf.append(tabs);
1081             buf.append(genHTMLForMonitors(sd, monitors));
1082         }
1083 
1084         buf.br();
1085         tabs.append(tab);
1086     }
1087 
1088     protected void genScObjInfo(Formatter buf, Formatter tabs, ScopeDesc sd) {
1089         if (sd == null) {
1090             return;
1091         }
1092 
1093         List objects = sd.getObjects();
1094         if (objects == null) {
1095             return;
1096         }
1097         int length = objects.size();
1098         for (int i = 0; i < length; i++) {
1099             buf.append(tabs);
1100             ObjectValue ov = (ObjectValue)objects.get(i);
1101             buf.append("ScObj" + i);
1102             ScopeValue sv = ov.getKlass();
1103             if (Assert.ASSERTS_ENABLED) {
1104                 Assert.that(sv.isConstantOop(), "scalar replaced object klass must be constant oop");
1105             }
1106             ConstantOopReadValue klv = (ConstantOopReadValue)sv;
1107             OopHandle klHandle = klv.getValue();
1108             if (Assert.ASSERTS_ENABLED) {
1109                 Assert.that(klHandle != null, "scalar replaced object klass must be not NULL");
1110             }
1111             Oop obj = VM.getVM().getObjectHeap().newOop(klHandle);
1112             // Obj is a Java mirror
1113             Klass klass = java_lang_Class.asKlass(obj);
1114             if (klass instanceof InstanceKlass) {
1115                 InstanceKlass kls = (InstanceKlass) klass;
1116                 buf.append(" " + kls.getName().asString() + "={");
1117                 int flen = ov.fieldsSize();
1118 
1119                 U2Array klfields = kls.getFields();
1120                 int klen = (int) klfields.length();
1121                 int findex = 0;
1122                 for (int index = 0; index < klen; index++) {
1123                     int accsFlags = kls.getFieldAccessFlags(index);
1124                     Symbol f_name = kls.getFieldName(index);
1125                     AccessFlags access = new AccessFlags(accsFlags);
1126                     if (!access.isStatic()) {
1127                         ScopeValue svf = ov.getFieldAt(findex++);
1128                         String    fstr = scopeValueAsString(sd, svf);
1129                         buf.append(" [" + f_name.asString() + " :"+ index + "]=(#" + fstr + ")");
1130                     }
1131                 }
1132                 buf.append(" }");
1133             } else {
1134                 buf.append(" ");
1135                 int flen = ov.fieldsSize();
1136                 if (klass instanceof TypeArrayKlass) {
1137                     TypeArrayKlass kls = (TypeArrayKlass) klass;
1138                     buf.append(kls.getElementTypeName() + "[" + flen + "]");
1139                 } else if (klass instanceof ObjArrayKlass) {
1140                     ObjArrayKlass kls = (ObjArrayKlass) klass;
1141                     Klass elobj = kls.getBottomKlass();
1142                     if (elobj instanceof InstanceKlass) {
1143                         buf.append(elobj.getName().asString());
1144                     } else if (elobj instanceof TypeArrayKlass) {
1145                         TypeArrayKlass elkls = (TypeArrayKlass) elobj;
1146                         buf.append(elkls.getElementTypeName());
1147                     } else {
1148                         if (Assert.ASSERTS_ENABLED) {
1149                             Assert.that(false, "unknown scalar replaced object klass!");
1150                         }
1151                     }
1152                     buf.append("[" + flen + "]");
1153                     int ndim = (int) kls.getDimension();
1154                     while (--ndim > 0) {
1155                         buf.append("[]");
1156                     }
1157                 } else {
1158                     if (Assert.ASSERTS_ENABLED) {
1159                         Assert.that(false, "unknown scalar replaced object klass!");
1160                     }
1161                 }
1162                 buf.append("={");
1163                 for (int findex = 0; findex < flen; findex++) {
1164                     ScopeValue svf = ov.getFieldAt(findex);
1165                     String fstr = scopeValueAsString(sd, svf);
1166                     buf.append(" [" + findex + "]=(#" + fstr + ")");
1167                 }
1168                 buf.append(" }");
1169             }
1170             buf.br();
1171         }
1172     }
1173 
1174    protected String genHTMLForOopMap(ImmutableOopMap map) {
1175       final int stack0 = VMRegImpl.getStack0().getValue();
1176       Formatter buf = new Formatter(genHTML);
1177 
1178       final class OopMapValueIterator {
1179          final Formatter iterate(OopMapStream oms, String type, boolean printContentReg) {
1180             Formatter tmpBuf = new Formatter(genHTML);
1181             boolean found = false;
1182             tmpBuf.beginTag("tr");
1183             tmpBuf.beginTag("td");
1184             tmpBuf.append(type);
1185             for (; ! oms.isDone(); oms.next()) {
1186                OopMapValue omv = oms.getCurrent();
1187                if (omv == null) {
1188                   continue;
1189                }
1190                found = true;
1191                VMReg vmReg = omv.getReg();
1192                int reg = vmReg.getValue();
1193                if (reg < stack0) {
1194                   tmpBuf.append(VMRegImpl.getRegisterName(reg));
1195                } else {
1196                   tmpBuf.append('[');
1197                   tmpBuf.append(Integer.toString((reg - stack0) * 4));
1198                   tmpBuf.append(']');
1199                }
1200                if (printContentReg) {
1201                   tmpBuf.append(" = ");
1202                   VMReg vmContentReg = omv.getContentReg();
1203                   int contentReg = vmContentReg.getValue();
1204                   if (contentReg < stack0) {
1205                      tmpBuf.append(VMRegImpl.getRegisterName(contentReg));
1206                   } else {
1207                      tmpBuf.append('[');
1208                      tmpBuf.append(Integer.toString((contentReg - stack0) * 4));
1209                      tmpBuf.append(']');
1210                   }
1211                }
1212                tmpBuf.append(spaces);
1213             }
1214             tmpBuf.endTag("td");
1215             tmpBuf.endTag("tr");
1216             return found ? tmpBuf : new Formatter(genHTML);
1217          }
1218       }
1219 
1220       buf.beginTable(0);
1221 
1222       OopMapValueIterator omvIterator = new OopMapValueIterator();
1223       OopMapStream oms = new OopMapStream(map, OopMapValue.OopTypes.OOP_VALUE);
1224       buf.append(omvIterator.iterate(oms, "Oops:", false));
1225 
1226       oms = new OopMapStream(map, OopMapValue.OopTypes.NARROWOOP_VALUE);
1227       buf.append(omvIterator.iterate(oms, "NarrowOops:", false));
1228 
1229       oms = new OopMapStream(map, OopMapValue.OopTypes.CALLEE_SAVED_VALUE);
1230       buf.append(omvIterator.iterate(oms, "Callee saved:",  true));
1231 
1232       oms = new OopMapStream(map, OopMapValue.OopTypes.DERIVED_OOP_VALUE);
1233       buf.append(omvIterator.iterate(oms, "Derived oops:", true));
1234 
1235       buf.endTag("table");
1236       return buf.toString();
1237    }
1238 
1239 
1240    protected String genOopMapInfo(NMethod nmethod, PCDesc pcDesc) {
1241       ImmutableOopMapSet mapSet = nmethod.getOopMaps();
1242       if (mapSet == null || (mapSet.getCount() <= 0))
1243         return "";
1244       int pcOffset = pcDesc.getPCOffset();
1245       ImmutableOopMap map = mapSet.findMapAtOffset(pcOffset, VM.getVM().isDebugging());
1246       if (map == null) {
1247          throw new IllegalArgumentException("no oopmap at safepoint!");
1248       }
1249 
1250       return genOopMapInfo(map);
1251    }
1252 
1253    protected String genOopMapInfo(ImmutableOopMap map) {
1254      Formatter buf = new Formatter(genHTML);
1255      buf.beginTag("pre");
1256      buf.append("OopMap: ");
1257      buf.br();
1258      buf.append(genHTMLForOopMap(map));
1259      buf.endTag("pre");
1260 
1261      return buf.toString();
1262    }
1263 
1264    protected String locationAsString(Location loc) {
1265       Formatter buf = new Formatter(genHTML);
1266       if (loc.isIllegal()) {
1267          buf.append("illegal");
1268       } else {
1269          Location.Where  w  = loc.getWhere();
1270          Location.Type type = loc.getType();
1271 
1272          if (w == Location.Where.ON_STACK) {
1273             buf.append("stack[" + loc.getStackOffset() + "]");
1274          } else if (w == Location.Where.IN_REGISTER) {
1275             boolean isFloat = (type == Location.Type.FLOAT_IN_DBL ||
1276                                type == Location.Type.DBL);
1277             int regNum = loc.getRegisterNumber();
1278             VMReg vmReg = new VMReg(regNum);
1279             buf.append(VMRegImpl.getRegisterName(vmReg.getValue()));
1280          }
1281 
1282          buf.append(", ");
1283          if (type == Location.Type.NORMAL) {
1284             buf.append("normal");
1285          } else if (type == Location.Type.OOP) {
1286             buf.append("oop");
1287          } else if (type == Location.Type.NARROWOOP) {
1288             buf.append("narrowoop");
1289          } else if (type == Location.Type.INT_IN_LONG) {
1290             buf.append("int");
1291          } else if (type == Location.Type.LNG) {
1292             buf.append("long");
1293          } else if (type == Location.Type.FLOAT_IN_DBL) {
1294             buf.append("float");
1295          } else if (type == Location.Type.DBL) {
1296             buf.append("double");
1297          } else if (type == Location.Type.ADDR) {
1298             buf.append("address");
1299          } else if (type == Location.Type.INVALID) {
1300             buf.append("invalid");
1301          }
1302       }
1303       return buf.toString();
1304    }
1305 
1306    private String scopeValueAsString(ScopeDesc sd, ScopeValue sv) {
1307       Formatter buf = new Formatter(genHTML);
1308       if (sv.isConstantInt()) {
1309          buf.append("int ");
1310          ConstantIntValue intValue = (ConstantIntValue) sv;
1311          buf.append(Integer.toString(intValue.getValue()));
1312       } else if (sv.isConstantLong()) {
1313          buf.append("long ");
1314          ConstantLongValue longValue = (ConstantLongValue) sv;
1315          buf.append(Long.toString(longValue.getValue()));
1316          buf.append("L");
1317       } else if (sv.isConstantDouble()) {
1318          buf.append("double ");
1319          ConstantDoubleValue dblValue = (ConstantDoubleValue) sv;
1320          buf.append(Double.toString(dblValue.getValue()));
1321          buf.append("D");
1322       } else if (sv.isConstantOop()) {
1323          buf.append("oop ");
1324          ConstantOopReadValue oopValue = (ConstantOopReadValue) sv;
1325          OopHandle oopHandle = oopValue.getValue();
1326          if (oopHandle != null) {
1327             buf.append(oopHandle.toString());
1328          } else {
1329             buf.append("null");
1330          }
1331       } else if (sv.isLocation()) {
1332          LocationValue lvalue = (LocationValue) sv;
1333          Location loc = lvalue.getLocation();
1334          if (loc != null) {
1335             buf.append(locationAsString(loc));
1336          } else {
1337             buf.append("null");
1338          }
1339       } else if (sv.isObject()) {
1340          ObjectValue ov = (ObjectValue)sv;
1341          buf.append("#ScObj" + sd.getObjects().indexOf(ov));
1342       } else {
1343          buf.append("unknown scope value " + sv);
1344       }
1345       return buf.toString();
1346    }
1347 
1348    protected String genHTMLForScopeValues(ScopeDesc sd, boolean locals, List values) {
1349       int length = values.size();
1350       Formatter buf = new Formatter(genHTML);
1351       buf.append(locals? "locals " : "expressions ");
1352       for (int i = 0; i < length; i++) {
1353          ScopeValue sv = (ScopeValue) values.get(i);
1354          if (sv == null) {
1355             continue;
1356          }
1357          buf.append('(');
1358          if (locals) {
1359             Symbol name = sd.getMethod().getLocalVariableName(sd.getBCI(), i);
1360             if (name != null) {
1361                buf.append("'");
1362                buf.append(name.asString());
1363                buf.append('\'');
1364             } else {
1365                buf.append("[");
1366                buf.append(Integer.toString(i));
1367                buf.append(']');
1368             }
1369          } else {
1370             buf.append("[");
1371             buf.append(Integer.toString(i));
1372             buf.append(']');
1373          }
1374 
1375          buf.append(", ");
1376          buf.append(scopeValueAsString(sd, sv));
1377          buf.append(") ");
1378       }
1379 
1380       return buf.toString();
1381    }
1382 
1383    protected String genHTMLForLocals(ScopeDesc sd, List locals) {
1384       return genHTMLForScopeValues(sd, true, locals);
1385    }
1386 
1387    protected String genHTMLForExpressions(ScopeDesc sd, List expressions) {
1388       return genHTMLForScopeValues(sd, false, expressions);
1389    }
1390 
1391    protected String genHTMLForMonitors(ScopeDesc sd, List monitors) {
1392       int length = monitors.size();
1393       Formatter buf = new Formatter(genHTML);
1394       buf.append("monitors ");
1395       for (int i = 0; i < length; i++) {
1396          MonitorValue mv = (MonitorValue) monitors.get(i);
1397          if (mv == null) {
1398             continue;
1399          }
1400          buf.append("(owner = ");
1401          ScopeValue owner = mv.owner();
1402          if (owner != null) {
1403             buf.append(scopeValueAsString(sd, owner));
1404          } else {
1405             buf.append("null");
1406          }
1407          buf.append(", lock = ");
1408 
1409          Location loc = mv.basicLock();
1410          if (loc != null) {
1411             buf.append(locationAsString(loc));
1412          } else {
1413             buf.append("null");
1414          }
1415          buf.append(") ");
1416       }
1417       return buf.toString();
1418    }
1419 
1420    public String genHTML(final NMethod nmethod) {
1421       try {
1422          final Formatter buf = new Formatter(genHTML);
1423          buf.genHTMLPrologue(genNMethodTitle(nmethod));
1424          buf.h3("Method");
1425          buf.append(genMethodAndKlassLink(nmethod.getMethod()));
1426 
1427          buf.h3("Compiled Code");
1428          Disassembler.decode(new HTMLDisassembler(buf, nmethod), nmethod);
1429          buf.genHTMLEpilogue();
1430          return buf.toString();
1431       } catch (Exception exp) {
1432          return genHTMLErrorMessage(exp);
1433       }
1434    }
1435 
1436   public String genHTML(final CodeBlob blob) {
1437       try {
1438          final Formatter buf = new Formatter(genHTML);
1439          buf.genHTMLPrologue(genCodeBlobTitle(blob));
1440          buf.h3("CodeBlob");
1441 
1442          buf.h3("Compiled Code");
1443          Disassembler.decode(new HTMLDisassembler(buf, blob), blob);
1444 
1445          buf.genHTMLEpilogue();
1446          return buf.toString();
1447       } catch (Exception exp) {
1448          return genHTMLErrorMessage(exp);
1449       }
1450    }
1451 
1452    protected String genInterpreterCodeletTitle(InterpreterCodelet codelet) {
1453       Formatter buf = new Formatter(genHTML);
1454       buf.append("Interpreter codelet [");
1455       buf.append(codelet.codeBegin().toString());
1456       buf.append(',');
1457       buf.append(codelet.codeEnd().toString());
1458       buf.append(") - ");
1459       buf.append(codelet.getDescription());
1460       return buf.toString();
1461    }
1462 
1463    protected String genInterpreterCodeletLinkPageHref(StubQueue stubq) {
1464       return genBaseHref() + "interp_codelets";
1465    }
1466 
1467    public String genInterpreterCodeletLinksPage() {
1468       Formatter buf = new Formatter(genHTML);
1469       buf.genHTMLPrologue("Interpreter Codelets");
1470       buf.beginTag("ul");
1471 
1472       Interpreter interp = VM.getVM().getInterpreter();
1473       StubQueue code = interp.getCode();
1474       InterpreterCodelet stub = (InterpreterCodelet) code.getFirst();
1475       while (stub != null) {
1476          buf.beginTag("li");
1477          sun.jvm.hotspot.debugger.Address addr = stub.codeBegin();
1478          buf.link(genPCHref(addressToLong(addr)), stub.getDescription() + " @" + addr);
1479          buf.endTag("li");
1480          stub = (InterpreterCodelet) code.getNext(stub);
1481       }
1482 
1483       buf.endTag("ul");
1484       buf.genHTMLEpilogue();
1485       return buf.toString();
1486    }
1487 
1488    public String genHTML(InterpreterCodelet codelet) {
1489       Formatter buf = new Formatter(genHTML);
1490       buf.genHTMLPrologue(genInterpreterCodeletTitle(codelet));
1491       Interpreter interp = VM.getVM().getInterpreter();
1492       StubQueue stubq = interp.getCode();
1493 
1494       if (genHTML) {
1495          buf.beginTag("h3");
1496          buf.link(genInterpreterCodeletLinkPageHref(stubq), "View links for all codelets");
1497          buf.endTag("h3");
1498          buf.br();
1499       }
1500 
1501       Stub prev = stubq.getPrev(codelet);
1502       if (prev != null) {
1503          if (genHTML) {
1504             buf.beginTag("h3");
1505             buf.link(genPCHref(addressToLong(prev.codeBegin())), "View Previous Codelet");
1506             buf.endTag("h3");
1507             buf.br();
1508          } else {
1509             buf.h3("Previous Codelet = 0x" + Long.toHexString(addressToLong(prev.codeBegin())));
1510          }
1511       }
1512 
1513       buf.h3("Code");
1514       Disassembler.decode(new HTMLDisassembler(buf, null), null,
1515                           codelet.codeBegin(), codelet.codeEnd());
1516 
1517       Stub next = stubq.getNext(codelet);
1518       if (next != null) {
1519          if (genHTML) {
1520             buf.beginTag("h3");
1521             buf.link(genPCHref(addressToLong(next.codeBegin())), "View Next Codelet");
1522             buf.endTag("h3");
1523          } else {
1524             buf.h3("Next Codelet = 0x" + Long.toHexString(addressToLong(next.codeBegin())));
1525          }
1526       }
1527 
1528       buf.genHTMLEpilogue();
1529       return buf.toString();
1530    }
1531 
1532    protected String genDumpKlassesTitle(InstanceKlass[] klasses) {
1533       return (klasses.length == 1) ? "Create .class for this class"
1534                                    : "Create .class for all classes";
1535    }
1536 
1537    protected String genDumpKlassesHref(InstanceKlass[] klasses) {
1538       StringBuffer buf = new StringBuffer(genBaseHref());
1539       buf.append("jcore_multiple=");
1540       for (int k = 0; k < klasses.length; k++) {
1541          buf.append(klasses[k].getAddress().toString());
1542          buf.append(',');
1543       }
1544       return buf.toString();
1545    }
1546 
1547    protected String genDumpKlassesLink(InstanceKlass[] klasses) {
1548       if (!genHTML) return "";
1549 
1550       Formatter buf = new Formatter(genHTML);
1551       buf.link(genDumpKlassesHref(klasses), genDumpKlassesTitle(klasses));
1552       return buf.toString();
1553    }
1554 
1555    public String genHTMLForKlassNames(InstanceKlass[] klasses) {
1556       try {
1557          Formatter buf = new Formatter(genHTML);
1558          buf.genHTMLPrologue();
1559          buf.h3(genDumpKlassesLink(klasses));
1560 
1561          buf.append(genHTMLListForKlassNames(klasses));
1562          buf.genHTMLEpilogue();
1563          return buf.toString();
1564       } catch (Exception exp) {
1565          return genHTMLErrorMessage(exp);
1566       }
1567    }
1568 
1569    protected String genHTMLListForKlassNames(InstanceKlass[] klasses) {
1570       final Formatter buf = new Formatter(genHTML);
1571       buf.beginTable(0);
1572       for (int i = 0; i < klasses.length; i++) {
1573          InstanceKlass ik = klasses[i];
1574          buf.beginTag("tr");
1575          buf.cell(genKlassLink(ik));
1576          buf.endTag("tr");
1577       }
1578 
1579       buf.endTable();
1580       return buf.toString();
1581    }
1582 
1583    public String genHTMLForMethodNames(InstanceKlass klass) {
1584       try {
1585          Formatter buf = new Formatter(genHTML);
1586          buf.genHTMLPrologue();
1587          buf.append(genHTMLListForMethods(klass));
1588          buf.genHTMLEpilogue();
1589          return buf.toString();
1590       } catch (Exception exp) {
1591          return genHTMLErrorMessage(exp);
1592       }
1593    }
1594 
1595    protected String genHTMLListForMethods(InstanceKlass klass) {
1596       Formatter buf = new Formatter(genHTML);
1597       MethodArray methods = klass.getMethods();
1598       int numMethods = methods.length();
1599       if (numMethods != 0) {
1600          buf.h3("Methods");
1601          buf.beginTag("ul");
1602          for (int m = 0; m < numMethods; m++) {
1603             Method mtd = methods.at(m);
1604             buf.li(genMethodLink(mtd) + ";");
1605          }
1606          buf.endTag("ul");
1607       }
1608       return buf.toString();
1609    }
1610 
1611    protected String genHTMLListForInterfaces(InstanceKlass klass) {
1612       try {
1613          Formatter buf = new Formatter(genHTML);
1614          KlassArray interfaces = klass.getLocalInterfaces();
1615          int numInterfaces = interfaces.length();
1616          if (numInterfaces != 0) {
1617             buf.h3("Interfaces");
1618             buf.beginTag("ul");
1619             for (int i = 0; i < numInterfaces; i++) {
1620                InstanceKlass inf = (InstanceKlass) interfaces.getAt(i);
1621                buf.li(genKlassLink(inf));
1622             }
1623             buf.endTag("ul");
1624          }
1625          return buf.toString();
1626       } catch (Exception exp) {
1627          return genHTMLErrorMessage(exp);
1628       }
1629    }
1630 
1631    protected String genFieldModifierString(AccessFlags acc) {
1632       Formatter buf = new Formatter(genHTML);
1633       if (acc.isPrivate()) {
1634          buf.append("private ");
1635       } else if (acc.isProtected()) {
1636          buf.append("protected ");
1637       } else if (acc.isPublic()) {
1638          buf.append("public ");
1639       }
1640 
1641       if (acc.isStatic()) {
1642          buf.append("static ");
1643       }
1644 
1645       if (acc.isFinal()) {
1646          buf.append("final ");
1647       }
1648       if (acc.isVolatile()) {
1649          buf.append("volatile ");
1650       }
1651       if (acc.isTransient()) {
1652          buf.append("transient ");
1653       }
1654 
1655       // javac generated flags
1656       if (acc.isSynthetic()) {
1657          buf.append("[synthetic] ");
1658       }
1659       return buf.toString();
1660    }
1661 
1662    public String genHTMLForFieldNames(InstanceKlass klass) {
1663       try {
1664          Formatter buf = new Formatter(genHTML);
1665          buf.genHTMLPrologue();
1666          buf.append(genHTMLListForFields(klass));
1667          buf.genHTMLEpilogue();
1668          return buf.toString();
1669       } catch (Exception exp) {
1670          return genHTMLErrorMessage(exp);
1671       }
1672    }
1673 
1674    protected String genHTMLListForFields(InstanceKlass klass) {
1675       Formatter buf = new Formatter(genHTML);
1676       U2Array fields = klass.getFields();
1677       int numFields = klass.getAllFieldsCount();
1678       if (numFields != 0) {
1679          buf.h3("Fields");
1680          buf.beginList();
1681          for (int f = 0; f < numFields; f++) {
1682            sun.jvm.hotspot.oops.Field field = klass.getFieldByIndex(f);
1683            String f_name = ((NamedFieldIdentifier)field.getID()).getName();
1684            Symbol f_sig  = field.getSignature();
1685            Symbol f_genSig = field.getGenericSignature();
1686            AccessFlags acc = field.getAccessFlagsObj();
1687 
1688            buf.beginListItem();
1689            buf.append(genFieldModifierString(acc));
1690            buf.append(' ');
1691            Formatter sigBuf = new Formatter(genHTML);
1692            new SignatureConverter(f_sig, sigBuf.getBuffer()).dispatchField();
1693            buf.append(sigBuf.toString().replace('/', '.'));
1694            buf.append(' ');
1695            buf.append(f_name);
1696            buf.append(';');
1697            // is it generic?
1698            if (f_genSig != null) {
1699               buf.append(" [signature ");
1700               buf.append(escapeHTMLSpecialChars(f_genSig.asString()));
1701               buf.append("] ");
1702            }
1703            buf.append(" (offset = " + field.getOffset() + ")");
1704            buf.endListItem();
1705          }
1706          buf.endList();
1707       }
1708       return buf.toString();
1709    }
1710 
1711    protected String genKlassHierarchyHref(InstanceKlass klass) {
1712       return genBaseHref() + "hierarchy=" + klass.getAddress();
1713    }
1714 
1715    protected String genKlassHierarchyTitle(InstanceKlass klass) {
1716       Formatter buf = new Formatter(genHTML);
1717       buf.append("Class Hierarchy of ");
1718       buf.append(genKlassTitle(klass));
1719       return buf.toString();
1720    }
1721 
1722    protected String genKlassHierarchyLink(InstanceKlass klass) {
1723       Formatter buf = new Formatter(genHTML);
1724       buf.link(genKlassHierarchyHref(klass), genKlassHierarchyTitle(klass));
1725       return buf.toString();
1726    }
1727 
1728    protected String genHTMLListForSubKlasses(InstanceKlass klass) {
1729       Formatter buf = new Formatter(genHTML);
1730       Klass subklass = klass.getSubklassKlass();
1731       if (subklass != null) {
1732          buf.beginList();
1733          while (subklass != null) {
1734             if (subklass instanceof InstanceKlass) {
1735                buf.li(genKlassLink((InstanceKlass)subklass));
1736             }
1737             subklass = subklass.getNextSiblingKlass();
1738          }
1739          buf.endList();
1740       }
1741       return buf.toString();
1742    }
1743 
1744    public String genHTMLForKlassHierarchy(InstanceKlass klass) {
1745       Formatter buf = new Formatter(genHTML);
1746       buf.genHTMLPrologue(genKlassHierarchyTitle(klass));
1747 
1748 
1749       buf.beginTag("pre");
1750       buf.append(genKlassLink(klass));
1751       buf.br();
1752       StringBuffer tabs = new StringBuffer(tab);
1753       InstanceKlass superKlass = klass;
1754       while ( (superKlass = (InstanceKlass) superKlass.getSuper()) != null ) {
1755          buf.append(tabs);
1756          buf.append(genKlassLink(superKlass));
1757          tabs.append(tab);
1758          buf.br();
1759       }
1760       buf.endTag("pre");
1761 
1762       // generate subklass list
1763       Klass subklass = klass.getSubklassKlass();
1764       if (subklass != null) {
1765          buf.h3("Direct Subclasses");
1766          buf.append(genHTMLListForSubKlasses(klass));
1767       }
1768 
1769       buf.genHTMLEpilogue();
1770       return buf.toString();
1771    }
1772 
1773    protected String genDumpKlassHref(InstanceKlass klass) {
1774       return genBaseHref() + "jcore=" + klass.getAddress();
1775    }
1776 
1777    protected String genDumpKlassLink(InstanceKlass klass) {
1778       if (!genHTML) return "";
1779 
1780       Formatter buf = new Formatter(genHTML);
1781       buf.link(genDumpKlassHref(klass), "Create .class File");
1782       return buf.toString();
1783    }
1784 
1785    public String genHTML(InstanceKlass klass) {
1786       Formatter buf = new Formatter(genHTML);
1787       buf.genHTMLPrologue(genKlassTitle(klass));
1788       InstanceKlass superKlass = (InstanceKlass) klass.getSuper();
1789 
1790       if (genHTML) {
1791           // super class tree and subclass list
1792           buf.beginTag("h3");
1793           buf.link(genKlassHierarchyHref(klass), "View Class Hierarchy");
1794           buf.endTag("h3");
1795       }
1796 
1797       // jcore - create .class link
1798       buf.h3(genDumpKlassLink(klass));
1799 
1800       // super class
1801       if (superKlass != null) {
1802          buf.h3("Super Class");
1803          buf.append(genKlassLink(superKlass));
1804       }
1805 
1806       // interfaces
1807       buf.append(genHTMLListForInterfaces(klass));
1808 
1809       // fields
1810       buf.append(genHTMLListForFields(klass));
1811 
1812       // methods
1813       buf.append(genHTMLListForMethods(klass));
1814 
1815       // constant pool link
1816       buf.h3("Constant Pool");
1817       buf.append(genConstantPoolLink(klass.getConstants()));
1818 
1819       buf.genHTMLEpilogue();
1820       return buf.toString();
1821    }
1822 
1823    protected sun.jvm.hotspot.debugger.Address parseAddress(String address) {
1824       VM vm = VM.getVM();
1825       sun.jvm.hotspot.debugger.Address addr = vm.getDebugger().parseAddress(address);
1826       return addr;
1827    }
1828 
1829    protected long addressToLong(sun.jvm.hotspot.debugger.Address addr) {
1830       return VM.getVM().getDebugger().getAddressValue(addr);
1831    }
1832 
1833    protected sun.jvm.hotspot.debugger.Address longToAddress(long addr) {
1834       return parseAddress("0x" + Long.toHexString(addr));
1835    }
1836 
1837    protected Oop getOopAtAddress(sun.jvm.hotspot.debugger.Address addr) {
1838       OopHandle oopHandle = addr.addOffsetToAsOopHandle(0);
1839       return VM.getVM().getObjectHeap().newOop(oopHandle);
1840    }
1841 
1842    protected Oop getOopAtAddress(String address) {
1843       sun.jvm.hotspot.debugger.Address addr = parseAddress(address);
1844       return getOopAtAddress(addr);
1845    }
1846 
1847    protected Klass getKlassAtAddress(String address) {
1848       sun.jvm.hotspot.debugger.Address addr = parseAddress(address);
1849       return (Klass)Metadata.instantiateWrapperFor(addr);
1850    }
1851 
1852    protected Method getMethodAtAddress(String address) {
1853       sun.jvm.hotspot.debugger.Address addr = parseAddress(address);
1854       return (Method)Metadata.instantiateWrapperFor(addr);
1855    }
1856 
1857    protected ConstantPool getConstantPoolAtAddress(String address) {
1858       sun.jvm.hotspot.debugger.Address addr = parseAddress(address);
1859       return (ConstantPool) Metadata.instantiateWrapperFor(addr);
1860    }
1861 
1862    private void dumpKlass(InstanceKlass kls) throws IOException {
1863       String klassName = kls.getName().asString();
1864       klassName = klassName.replace('/', File.separatorChar);
1865       int index = klassName.lastIndexOf(File.separatorChar);
1866       File dir = null;
1867       if (index != -1) {
1868         String dirName = klassName.substring(0, index);
1869         dir =  new File(DUMP_KLASS_OUTPUT_DIR,  dirName);
1870       } else {
1871         dir = new File(DUMP_KLASS_OUTPUT_DIR);
1872       }
1873 
1874       dir.mkdirs();
1875       File f = new File(dir, klassName.substring(klassName.lastIndexOf(File.separatorChar) + 1)
1876                               + ".class");
1877       f.createNewFile();
1878       FileOutputStream fis = new FileOutputStream(f);
1879       ClassWriter cw = new ClassWriter(kls, fis);
1880       cw.write();
1881    }
1882 
1883    public String genDumpKlass(InstanceKlass kls) {
1884       try {
1885          dumpKlass(kls);
1886          Formatter buf = new Formatter(genHTML);
1887          buf.genHTMLPrologue(genKlassTitle(kls));
1888          buf.append(".class created for ");
1889          buf.append(genKlassLink(kls));
1890          buf.genHTMLEpilogue();
1891          return buf.toString();
1892       } catch(IOException exp) {
1893          return genHTMLErrorMessage(exp);
1894       }
1895    }
1896 
1897    protected String genJavaStackTraceTitle(JavaThread thread) {
1898       Formatter buf = new Formatter(genHTML);
1899       buf.append("Java Stack Trace for ");
1900       buf.append(thread.getThreadName());
1901       return buf.toString();
1902    }
1903 
1904    public String genHTMLForJavaStackTrace(JavaThread thread) {
1905       Formatter buf = new Formatter(genHTML);
1906       buf.genHTMLPrologue(genJavaStackTraceTitle(thread));
1907 
1908       buf.append("Thread state = ");
1909       buf.append(thread.getThreadState().toString());
1910       buf.br();
1911       buf.beginTag("pre");
1912       for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
1913          Method method = vf.getMethod();
1914          buf.append(" - ");
1915          buf.append(genMethodLink(method));
1916          buf.append(" @bci = " + vf.getBCI());
1917 
1918          int lineNumber = method.getLineNumberFromBCI(vf.getBCI());
1919          if (lineNumber != -1) {
1920             buf.append(", line = ");
1921             buf.append(lineNumber);
1922          }
1923 
1924          sun.jvm.hotspot.debugger.Address pc = vf.getFrame().getPC();
1925          if (pc != null) {
1926             buf.append(", pc = ");
1927             buf.link(genPCHref(addressToLong(pc)), pc.toString());
1928          }
1929 
1930          if (!method.isStatic() && !method.isNative()) {
1931             OopHandle oopHandle = vf.getLocals().oopHandleAt(0);
1932 
1933             if (oopHandle != null) {
1934                buf.append(", oop = ");
1935                buf.append(oopHandle.toString());
1936             }
1937          }
1938 
1939          if (vf.isCompiledFrame()) {
1940             buf.append(" (Compiled");
1941          }
1942          else if (vf.isInterpretedFrame()) {
1943             buf.append(" (Interpreted");
1944          }
1945 
1946          if (vf.mayBeImpreciseDbg()) {
1947             buf.append("; information may be imprecise");
1948          }
1949          buf.append(")");
1950          buf.br();
1951       }
1952 
1953       buf.endTag("pre");
1954       buf.genHTMLEpilogue();
1955       return buf.toString();
1956    }
1957 
1958    public String genHTMLForHyperlink(String href) {
1959       if (href.startsWith("klass=")) {
1960          href = href.substring(href.indexOf('=') + 1);
1961          Klass k = getKlassAtAddress(href);
1962          if (Assert.ASSERTS_ENABLED) {
1963             Assert.that(k instanceof InstanceKlass, "class= href with improper InstanceKlass!");
1964          }
1965          return genHTML((InstanceKlass) k);
1966       } else if (href.startsWith("method=")) {
1967          href = href.substring(href.indexOf('=') + 1);
1968          Method obj = getMethodAtAddress(href);
1969          if (Assert.ASSERTS_ENABLED) {
1970             Assert.that(obj instanceof Method, "method= href with improper Method!");
1971          }
1972          return genHTML(obj);
1973       } else if (href.startsWith("nmethod=")) {
1974          String addr = href.substring(href.indexOf('=') + 1);
1975          Object obj = VMObjectFactory.newObject(NMethod.class, parseAddress(addr));
1976          if (Assert.ASSERTS_ENABLED) {
1977             Assert.that(obj instanceof NMethod, "nmethod= href with improper NMethod!");
1978          }
1979          return genHTML((NMethod) obj);
1980       } else if (href.startsWith("pc=")) {
1981          String address = href.substring(href.indexOf('=') + 1);
1982          return genHTML(parseAddress(address));
1983       } else if (href.startsWith("pc_multiple=")) {
1984          int indexOfComma = href.indexOf(',');
1985          if (indexOfComma == -1) {
1986             String firstPC = href.substring(href.indexOf('=') + 1);
1987             return genHTMLForRawDisassembly(parseAddress(firstPC), null);
1988          } else {
1989             String firstPC = href.substring(href.indexOf('=') + 1, indexOfComma);
1990             return genHTMLForRawDisassembly(parseAddress(firstPC), href.substring(indexOfComma + 1));
1991          }
1992       } else if (href.startsWith("interp_codelets")) {
1993          return genInterpreterCodeletLinksPage();
1994       } else if (href.startsWith("hierarchy=")) {
1995          href = href.substring(href.indexOf('=') + 1);
1996          Klass obj = getKlassAtAddress(href);
1997          if (Assert.ASSERTS_ENABLED) {
1998             Assert.that(obj instanceof InstanceKlass, "class= href with improper InstanceKlass!");
1999          }
2000          return genHTMLForKlassHierarchy((InstanceKlass) obj);
2001       } else if (href.startsWith("cpool=")) {
2002          href = href.substring(href.indexOf('=') + 1);
2003          ConstantPool obj = getConstantPoolAtAddress(href);
2004          if (Assert.ASSERTS_ENABLED) {
2005             Assert.that(obj instanceof ConstantPool, "cpool= href with improper ConstantPool!");
2006          }
2007          return genHTML(obj);
2008       } else if (href.startsWith("jcore=")) {
2009          href = href.substring(href.indexOf('=') + 1);
2010          Klass obj = getKlassAtAddress(href);
2011          if (Assert.ASSERTS_ENABLED) {
2012             Assert.that(obj instanceof InstanceKlass, "jcore= href with improper InstanceKlass!");
2013          }
2014          return genDumpKlass((InstanceKlass) obj);
2015       } else if (href.startsWith("jcore_multiple=")) {
2016          href = href.substring(href.indexOf('=') + 1);
2017          Formatter buf = new Formatter(genHTML);
2018          buf.genHTMLPrologue();
2019          StringTokenizer st = new StringTokenizer(href, ",");
2020          while (st.hasMoreTokens()) {
2021             Klass obj = getKlassAtAddress(st.nextToken());
2022             if (Assert.ASSERTS_ENABLED) {
2023                Assert.that(obj instanceof InstanceKlass, "jcore_multiple= href with improper InstanceKlass!");
2024             }
2025 
2026             InstanceKlass kls = (InstanceKlass) obj;
2027             try {
2028                dumpKlass(kls);
2029                buf.append(".class created for ");
2030                buf.append(genKlassLink(kls));
2031             } catch(Exception exp) {
2032                buf.bold("can't .class for " +
2033                         genKlassTitle(kls) +
2034                         " : " +
2035                         exp.getMessage());
2036             }
2037             buf.br();
2038          }
2039 
2040          buf.genHTMLEpilogue();
2041          return buf.toString();
2042       } else {
2043          if (Assert.ASSERTS_ENABLED) {
2044             Assert.that(false, "unknown href link!");
2045          }
2046          return null;
2047       }
2048    }
2049 }