/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.bytecode; import java.io.*; import com.sun.max.lang.*; import com.sun.max.program.*; import com.sun.max.vm.classfile.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.classfile.stackmap.*; import com.sun.max.vm.verifier.*; /** * Facilities for printing the details of a {@link CodeAttribute} in a textual format. This * includes support for disassembling the {@linkplain CodeAttribute#code()}. */ public final class CodeAttributePrinter { private CodeAttributePrinter() { } /** * Formats the contents of a given CodeAttribute to a string. */ public static String toString(CodeAttribute codeAttribute) { CharArrayWriter charArrayWriter = new CharArrayWriter(); print(charArrayWriter, codeAttribute); return charArrayWriter.toString(); } /** * Prints the contents of a given CodeAttribute in a textual format to a given print stream. * * @param stream where to print the contents * @param codeAttribute the object to print */ public static void print(OutputStream stream, CodeAttribute codeAttribute) { final PrintWriter writer = new PrintWriter(stream); print(writer, codeAttribute); } /** * Prints the contents of a given CodeAttribute in a textual format to a given print writer. * * @param writer where to print the contents * @param codeAttribute the object to print */ public static void print(Writer writer, CodeAttribute codeAttribute) { final PrintWriter printWriter = (writer instanceof PrintWriter) ? (PrintWriter) writer : new PrintWriter(writer); printWriter.println("Stack=" + (int) codeAttribute.maxStack + ", Locals=" + (int) codeAttribute.maxLocals); final BytecodePrinter bytecodePrinter = new BytecodePrinter(printWriter, codeAttribute.cp); bytecodePrinter.setLineNumberTable(codeAttribute.lineNumberTable()); final BytecodeScanner bytecodeScanner = new BytecodeScanner(bytecodePrinter); try { bytecodeScanner.scan(new BytecodeBlock(codeAttribute.code())); // Print the attributes within the CodeAttribute printExceptionHandlerTable(codeAttribute, printWriter); printStackMapTable(codeAttribute, printWriter); printLineNumberTable(codeAttribute, printWriter); printLocalVariableTable(codeAttribute, printWriter); } catch (Throwable throwable) { printWriter.flush(); ProgramWarning.message("could not print bytecodes: " + throwable); } printWriter.flush(); } /** * Prints the contents of the {@linkplain CodeAttribute#exceptionHandlerTable() exception table} in a given * code attribute object to a given print writer. This method outputs nothing to {@code printWriter} if the exception * table is empty. * * @param codeAttribute * a code attribute that contains a (possibly empty) exception table * @param printWriter * where to print the contents of the exception table */ public static void printExceptionHandlerTable(CodeAttribute codeAttribute, final PrintWriter printWriter) { final ExceptionHandlerEntry[] exceptionHandlerTable = codeAttribute.exceptionHandlerTable(); if (exceptionHandlerTable.length != 0) { printWriter.println("Exception Handlers:"); printWriter.println(" from to target type"); for (ExceptionHandlerEntry entry : exceptionHandlerTable) { String catchType; final int catchTypeIndex = entry.catchTypeIndex(); if (catchTypeIndex == 0) { catchType = "*any*"; } else { try { catchType = codeAttribute.cp.classAt(catchTypeIndex).typeDescriptor().toJavaString(); } catch (ClassFormatError classFormatError) { catchType = "*ERROR[cpi=" + catchTypeIndex + "]*"; } } printWriter.println(String.format(" %-6d%-4d%-8d%s", entry.startBCI(), entry.endBCI(), entry.handlerBCI(), catchType)); } } printWriter.flush(); } /** * Prints the contents of the {@linkplain CodeAttribute#stackMapTable() stack map table} in a given code attribute * object to a given print writer. This method outputs nothing to {@code printWriter} if the stack map table is * empty or does not exist. * * @param codeAttribute a code attribute that contains a (possibly null) stack map table * @param printWriter where to print the contents of the stack map table */ public static void printStackMapTable(CodeAttribute codeAttribute, final PrintWriter printWriter) { final StackMapTable stackMapTable = codeAttribute.stackMapTable(); if (stackMapTable != null) { final Verifier verifier = new Verifier(codeAttribute.cp); final StackMapFrame[] frames = stackMapTable.getFrames(verifier); printWriter.println("StackMapTable: number of entries = " + frames.length); int previousFrameOffset = -1; for (int i = 0; i != frames.length; ++i) { final StackMapFrame stackMapFrame = frames[i]; final int offset = stackMapFrame.getBCI(previousFrameOffset); printWriter.println(Strings.indent(offset + ": " + stackMapFrame.toString(), " ")); previousFrameOffset = offset; } } printWriter.flush(); } /** * Prints the contents of the {@linkplain CodeAttribute#lineNumberTable() line number table} in a given code * attribute object to a given print writer. This method outputs nothing to {@code printWriter} if the line number * table is empty. * * @param codeAttribute a code attribute that contains a (possibly empty) line number table * @param printWriter where to print the contents of the line number table */ public static void printLineNumberTable(CodeAttribute codeAttribute, final PrintWriter printWriter) { final LineNumberTable lineNumberTable = codeAttribute.lineNumberTable(); if (!lineNumberTable.isEmpty()) { printWriter.println("LineNumberTable:"); for (LineNumberTable.Entry entry : lineNumberTable.entries()) { printWriter.println(" line " + entry.lineNumber() + ": " + entry.bci()); } } printWriter.flush(); } /** * Prints the contents of the {@linkplain CodeAttribute#localVariableTable() local variable table} in a given code * attribute object to a given print writer. This method outputs nothing to {@code printWriter} if the local * variable table is empty. * * @param codeAttribute a code attribute that contains a (possibly empty) local variable table * @param printWriter where to print the contents of the local variable table */ public static void printLocalVariableTable(CodeAttribute codeAttribute, final PrintWriter printWriter) { final LocalVariableTable localVariableTable = codeAttribute.localVariableTable(); if (!localVariableTable.isEmpty()) { printWriter.println("LocalVariableTable:"); printWriter.println(" Start Length Slot Name Descriptor Generic-signature"); final ConstantPool cp = codeAttribute.cp; for (LocalVariableTable.Entry entry : localVariableTable.entries()) { final int signatureIndex = entry.signatureIndex(); final String name = utf8At(cp, entry.nameIndex()); final String descriptor = utf8At(cp, entry.descriptorIndex()); final String genericSignature = signatureIndex == 0 ? "" : utf8At(cp, signatureIndex); printWriter.println(String.format(" %-6d%-7d%-5d%-19s%-22s%s", entry.startBCI(), entry.length(), entry.slot(), name, descriptor, genericSignature)); } } printWriter.flush(); } /** * Gets a UTF8 string from a given constant pool. If a {@code ClassFormatError} occurs while retrieving the UTF8 * value, then the result of calling {@link ClassFormatError#toString()} on the error object is return instead. * * @param constantPool * @param index * @return the string version of the UTF8 at {@code index} in {@code constantPool} */ public static String utf8At(ConstantPool constantPool, int index) { try { return constantPool.utf8At(index, null).toString(); } catch (ClassFormatError classFormatError) { return classFormatError.toString(); } } }