blob: b9bbb66a4b870b8b28b6872aa74d9d1879b2ffc3 [file] [log] [blame]
/*
* Copyright 2002-2005 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.tools.javap;
import java.util.*;
import java.io.*;
import static sun.tools.javap.RuntimeConstants.*;
/**
* Program to print information about class files
*
* @author Sucheta Dambalkar
*/
public class JavapPrinter {
JavapEnvironment env;
ClassData cls;
byte[] code;
String lP= "";
PrintWriter out;
public JavapPrinter(InputStream cname, PrintWriter out, JavapEnvironment env){
this.out = out;
this.cls = new ClassData(cname);
this.env = env;
}
/**
* Entry point to print class file information.
*/
public void print(){
printclassHeader();
printfields();
printMethods();
printend();
}
/**
* Print a description of the class (not members).
*/
public void printclassHeader(){
String srcName="";
if ((srcName = cls.getSourceName()) != "null") // requires debug info
out.println("Compiled from " + javaclassname(srcName));
if(cls.isInterface()) {
// The only useful access modifier of an interface is
// public; interfaces are always marked as abstract and
// cannot be final.
out.print((cls.isPublic()?"public ":"") +
"interface "+ javaclassname(cls.getClassName()));
}
else if(cls.isClass()) {
String []accflags = cls.getAccess();
printAccess(accflags);
out.print("class "+ javaclassname(cls.getClassName()));
if(cls.getSuperClassName() != null){
out.print(" extends " + javaclassname(cls.getSuperClassName()));
}
}
String []interfacelist = cls.getSuperInterfaces();
if(interfacelist.length > 0){
if(cls.isClass()) {
out.print(" implements ");
}
else if(cls.isInterface()){
out.print(" extends ");
}
for(int j = 0; j < interfacelist.length; j++){
out.print(javaclassname(interfacelist[j]));
if((j+1) < interfacelist.length) {
out.print(",");
}
}
}
// Print class attribute information.
if((env.showallAttr) || (env.showVerbose)){
printClassAttributes();
}
// Print verbose output.
if(env.showVerbose){
printverbosecls();
}
out.println("{");
}
/**
* Print verbose output.
*/
public void printverbosecls(){
out.println(" minor version: "+cls.getMinor_version());
out.println(" major version: "+cls.getMajor_version());
out.println(" Constant pool:");
printcp();
env.showallAttr = true;
}
/**
* Print class attribute information.
*/
public void printClassAttributes(){
out.println();
AttrData[] clsattrs = cls.getAttributes();
for(int i = 0; i < clsattrs.length; i++){
String clsattrname = clsattrs[i].getAttrName();
if(clsattrname.equals("SourceFile")){
out.println(" SourceFile: "+ cls.getSourceName());
}else if(clsattrname.equals("InnerClasses")){
printInnerClasses();
}else {
printAttrData(clsattrs[i]);
}
}
}
/**
* Print the fields
*/
public void printfields(){
FieldData[] fields = cls.getFields();
for(int f = 0; f < fields.length; f++){
String[] accflags = fields[f].getAccess();
if(checkAccess(accflags)){
if(!(env. showLineAndLocal || env.showDisassembled || env.showVerbose
|| env.showInternalSigs || env.showallAttr)){
out.print(" ");
}
printAccess(accflags);
out.println(fields[f].getType()+" " +fields[f].getName()+";");
if (env.showInternalSigs) {
out.println(" Signature: " + (fields[f].getInternalSig()));
}
// print field attribute information.
if (env.showallAttr){
printFieldAttributes(fields[f]);
}
if((env.showDisassembled) || (env.showLineAndLocal)){
out.println();
}
}
}
}
/* print field attribute information. */
public void printFieldAttributes(FieldData field){
Vector fieldattrs = field.getAttributes();
for(int j = 0; j < fieldattrs.size(); j++){
String fieldattrname = ((AttrData)fieldattrs.elementAt(j)).getAttrName();
if(fieldattrname.equals("ConstantValue")){
printConstantValue(field);
}else if (fieldattrname.equals("Deprecated")){
out.println("Deprecated: "+ field.isDeprecated());
}else if (fieldattrname.equals("Synthetic")){
out.println(" Synthetic: "+ field.isSynthetic());
}else {
printAttrData((AttrData)fieldattrs.elementAt(j));
}
}
out.println();
}
/**
* Print the methods
*/
public void printMethods(){
MethodData[] methods = cls.getMethods();
for(int m = 0; m < methods.length; m++){
String[] accflags = methods[m].getAccess();
if(checkAccess(accflags)){
if(!(env. showLineAndLocal || env.showDisassembled || env.showVerbose
|| env.showInternalSigs || env.showallAttr)){
out.print(" ");
}
printMethodSignature(methods[m], accflags);
printExceptions(methods[m]);
out.println(";");
// Print internal signature of method.
if (env.showInternalSigs){
out.println(" Signature: " + (methods[m].getInternalSig()));
}
//Print disassembled code.
if(env.showDisassembled && ! env.showallAttr) {
printcodeSequence(methods[m]);
printExceptionTable(methods[m]);
out.println();
}
// Print line and local variable attribute information.
if (env.showLineAndLocal) {
printLineNumTable(methods[m]);
printLocVarTable(methods[m]);
out.println();
}
// Print method attribute information.
if (env.showallAttr){
printMethodAttributes(methods[m]);
}
}
}
}
/**
* Print method signature.
*/
public void printMethodSignature(MethodData method, String[] accflags){
printAccess(accflags);
if((method.getName()).equals("<init>")){
out.print(javaclassname(cls.getClassName()));
out.print(method.getParameters());
}else if((method.getName()).equals("<clinit>")){
out.print("{}");
}else{
out.print(method.getReturnType()+" ");
out.print(method.getName());
out.print(method.getParameters());
}
}
/**
* print method attribute information.
*/
public void printMethodAttributes(MethodData method){
Vector methodattrs = method.getAttributes();
Vector codeattrs = method.getCodeAttributes();
for(int k = 0; k < methodattrs.size(); k++){
String methodattrname = ((AttrData)methodattrs.elementAt(k)).getAttrName();
if(methodattrname.equals("Code")){
printcodeSequence(method);
printExceptionTable(method);
for(int c = 0; c < codeattrs.size(); c++){
String codeattrname = ((AttrData)codeattrs.elementAt(c)).getAttrName();
if(codeattrname.equals("LineNumberTable")){
printLineNumTable(method);
}else if(codeattrname.equals("LocalVariableTable")){
printLocVarTable(method);
}else if(codeattrname.equals("StackMapTable")) {
// Java SE JSR 202 stack map tables
printStackMapTable(method);
}else if(codeattrname.equals("StackMap")) {
// Java ME CLDC stack maps
printStackMap(method);
} else {
printAttrData((AttrData)codeattrs.elementAt(c));
}
}
}else if(methodattrname.equals("Exceptions")){
out.println(" Exceptions: ");
printExceptions(method);
}else if (methodattrname.equals("Deprecated")){
out.println(" Deprecated: "+ method.isDeprecated());
}else if (methodattrname.equals("Synthetic")){
out.println(" Synthetic: "+ method.isSynthetic());
}else {
printAttrData((AttrData)methodattrs.elementAt(k));
}
}
out.println();
}
/**
* Print exceptions.
*/
public void printExceptions(MethodData method){
int []exc_index_table = method.get_exc_index_table();
if (exc_index_table != null) {
if(!(env. showLineAndLocal || env.showDisassembled || env.showVerbose
|| env.showInternalSigs || env.showallAttr)){
out.print(" ");
}
out.print(" throws ");
int k;
int l = exc_index_table.length;
for (k=0; k<l; k++) {
out.print(javaclassname(cls.getClassName(exc_index_table[k])));
if (k<l-1) out.print(", ");
}
}
}
/**
* Print code sequence.
*/
public void printcodeSequence(MethodData method){
code = method.getCode();
if(code != null){
out.println(" Code:");
if(env.showVerbose){
printVerboseHeader(method);
}
for (int pc=0; pc < code.length; ) {
out.print(" "+pc+":\t");
pc=pc+printInstr(pc);
out.println();
}
}
}
/**
* Print instructions.
*/
public int printInstr(int pc){
int opcode = getUbyte(pc);
int opcode2;
String mnem;
switch (opcode) {
case opc_nonpriv:
case opc_priv:
opcode2 = getUbyte(pc+1);
mnem=Tables.opcName((opcode<<8)+opcode2);
if (mnem==null)
// assume all (even nonexistent) priv and nonpriv instructions
// are 2 bytes long
mnem=Tables.opcName(opcode)+" "+opcode2;
out.print(mnem);
return 2;
case opc_wide: {
opcode2 = getUbyte(pc+1);
mnem=Tables.opcName((opcode<<8)+opcode2);
if (mnem==null) {
// nonexistent opcode - but we have to print something
out.print("bytecode "+opcode);
return 1;
}
out.print(mnem+" "+getUShort(pc+2));
if (opcode2==opc_iinc) {
out.print(", "+getShort(pc+4));
return 6;
}
return 4;
}
}
mnem=Tables.opcName(opcode);
if (mnem==null) {
// nonexistent opcode - but we have to print something
out.print("bytecode "+opcode);
return 1;
}
if (opcode>opc_jsr_w) {
// pseudo opcodes should be printed as bytecodes
out.print("bytecode "+opcode);
return 1;
}
out.print(Tables.opcName(opcode));
switch (opcode) {
case opc_aload: case opc_astore:
case opc_fload: case opc_fstore:
case opc_iload: case opc_istore:
case opc_lload: case opc_lstore:
case opc_dload: case opc_dstore:
case opc_ret:
out.print("\t"+getUbyte(pc+1));
return 2;
case opc_iinc:
out.print("\t"+getUbyte(pc+1)+", "+getbyte(pc+2));
return 3;
case opc_tableswitch:{
int tb=align(pc+1);
int default_skip = getInt(tb); /* default skip pamount */
int low = getInt(tb+4);
int high = getInt(tb+8);
int count = high - low;
out.print("{ //"+low+" to "+high);
for (int i = 0; i <= count; i++)
out.print( "\n\t\t" + (i+low) + ": "+lP+(pc+getInt(tb+12+4*i))+";");
out.print("\n\t\tdefault: "+lP+(default_skip + pc) + " }");
return tb-pc+16+count*4;
}
case opc_lookupswitch:{
int tb=align(pc+1);
int default_skip = getInt(tb);
int npairs = getInt(tb+4);
out.print("{ //"+npairs);
for (int i = 1; i <= npairs; i++)
out.print("\n\t\t"+getInt(tb+i*8)
+": "+lP+(pc+getInt(tb+4+i*8))+";"
);
out.print("\n\t\tdefault: "+lP+(default_skip + pc) + " }");
return tb-pc+(npairs+1)*8;
}
case opc_newarray:
int type=getUbyte(pc+1);
switch (type) {
case T_BOOLEAN:out.print(" boolean");break;
case T_BYTE: out.print(" byte"); break;
case T_CHAR: out.print(" char"); break;
case T_SHORT: out.print(" short"); break;
case T_INT: out.print(" int"); break;
case T_LONG: out.print(" long"); break;
case T_FLOAT: out.print(" float"); break;
case T_DOUBLE: out.print(" double"); break;
case T_CLASS: out.print(" class"); break;
default: out.print(" BOGUS TYPE:"+type);
}
return 2;
case opc_anewarray: {
int index = getUShort(pc+1);
out.print("\t#"+index+"; //");
PrintConstant(index);
return 3;
}
case opc_sipush:
out.print("\t"+getShort(pc+1));
return 3;
case opc_bipush:
out.print("\t"+getbyte(pc+1));
return 2;
case opc_ldc: {
int index = getUbyte(pc+1);
out.print("\t#"+index+"; //");
PrintConstant(index);
return 2;
}
case opc_ldc_w: case opc_ldc2_w:
case opc_instanceof: case opc_checkcast:
case opc_new:
case opc_putstatic: case opc_getstatic:
case opc_putfield: case opc_getfield:
case opc_invokevirtual:
case opc_invokespecial:
case opc_invokestatic: {
int index = getUShort(pc+1);
out.print("\t#"+index+"; //");
PrintConstant(index);
return 3;
}
case opc_invokeinterface: {
int index = getUShort(pc+1), nargs=getUbyte(pc+3);
out.print("\t#"+index+", "+nargs+"; //");
PrintConstant(index);
return 5;
}
case opc_multianewarray: {
int index = getUShort(pc+1), dimensions=getUbyte(pc+3);
out.print("\t#"+index+", "+dimensions+"; //");
PrintConstant(index);
return 4;
}
case opc_jsr: case opc_goto:
case opc_ifeq: case opc_ifge: case opc_ifgt:
case opc_ifle: case opc_iflt: case opc_ifne:
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
case opc_if_acmpeq: case opc_if_acmpne:
case opc_ifnull: case opc_ifnonnull:
out.print("\t"+lP+(pc + getShort(pc+1)) );
return 3;
case opc_jsr_w:
case opc_goto_w:
out.print("\t"+lP+(pc + getInt(pc+1)));
return 5;
default:
return 1;
}
}
/**
* Print code attribute details.
*/
public void printVerboseHeader(MethodData method) {
int argCount = method.getArgumentlength();
if (!method.isStatic())
++argCount; // for 'this'
out.println(" Stack=" + method.getMaxStack()
+ ", Locals=" + method.getMaxLocals()
+ ", Args_size=" + argCount);
}
/**
* Print the exception table for this method code
*/
void printExceptionTable(MethodData method){//throws IOException
Vector exception_table = method.getexception_table();
if (exception_table.size() > 0) {
out.println(" Exception table:");
out.println(" from to target type");
for (int idx = 0; idx < exception_table.size(); ++idx) {
TrapData handler = (TrapData)exception_table.elementAt(idx);
printFixedWidthInt(handler.start_pc, 6);
printFixedWidthInt(handler.end_pc, 6);
printFixedWidthInt(handler.handler_pc, 6);
out.print(" ");
int catch_cpx = handler.catch_cpx;
if (catch_cpx == 0) {
out.println("any");
}else {
out.print("Class ");
out.println(cls.getClassName(catch_cpx));
out.println("");
}
}
}
}
/**
* Print LineNumberTable attribute information.
*/
public void printLineNumTable(MethodData method) {
int numlines = method.getnumlines();
Vector lin_num_tb = method.getlin_num_tb();
if( lin_num_tb.size() > 0){
out.println(" LineNumberTable: ");
for (int i=0; i<numlines; i++) {
LineNumData linnumtb_entry=(LineNumData)lin_num_tb.elementAt(i);
out.println(" line " + linnumtb_entry.line_number + ": "
+ linnumtb_entry.start_pc);
}
}
out.println();
}
/**
* Print LocalVariableTable attribute information.
*/
public void printLocVarTable(MethodData method){
int siz = method.getloc_var_tbsize();
if(siz > 0){
out.println(" LocalVariableTable: ");
out.print(" ");
out.println("Start Length Slot Name Signature");
}
Vector loc_var_tb = method.getloc_var_tb();
for (int i=0; i<siz; i++) {
LocVarData entry=(LocVarData)loc_var_tb.elementAt(i);
out.println(" "+entry.start_pc+" "+entry.length+" "+
entry.slot+" "+cls.StringValue(entry.name_cpx) +
" "+cls.StringValue(entry.sig_cpx));
}
out.println();
}
/**
* Print StackMap attribute information.
*/
public void printStackMap(MethodData method) {
StackMapData[] stack_map_tb = method.getStackMap();
int number_of_entries = stack_map_tb.length;
if (number_of_entries > 0) {
out.println(" StackMap: number_of_entries = " + number_of_entries);
for (StackMapData frame : stack_map_tb) {
frame.print(this);
}
}
out.println();
}
/**
* Print StackMapTable attribute information.
*/
public void printStackMapTable(MethodData method) {
StackMapTableData[] stack_map_tb = method.getStackMapTable();
int number_of_entries = stack_map_tb.length;
if (number_of_entries > 0) {
out.println(" StackMapTable: number_of_entries = " + number_of_entries);
for (StackMapTableData frame : stack_map_tb) {
frame.print(this);
}
}
out.println();
}
void printMap(String name, int[] map) {
out.print(name);
for (int i=0; i<map.length; i++) {
int fulltype = map[i];
int type = fulltype & 0xFF;
int argument = fulltype >> 8;
switch (type) {
case ITEM_Object:
out.print(" ");
PrintConstant(argument);
break;
case ITEM_NewObject:
out.print(" " + Tables.mapTypeName(type));
out.print(" " + argument);
break;
default:
out.print(" " + Tables.mapTypeName(type));
}
out.print( (i==(map.length-1)? ' ' : ','));
}
out.println("]");
}
/**
* Print ConstantValue attribute information.
*/
public void printConstantValue(FieldData field){
out.print(" Constant value: ");
int cpx = (field.getConstantValueIndex());
byte tag=0;
try {
tag=cls.getTag(cpx);
} catch (IndexOutOfBoundsException e) {
out.print("Error:");
return;
}
switch (tag) {
case CONSTANT_METHOD:
case CONSTANT_INTERFACEMETHOD:
case CONSTANT_FIELD: {
CPX2 x = (CPX2)(cls.getCpoolEntry(cpx));
if (x.cpx1 == cls.getthis_cpx()) {
// don't print class part for local references
cpx=x.cpx2;
}
}
}
out.print(cls.TagString(tag)+" "+ cls.StringValue(cpx));
}
/**
* Print InnerClass attribute information.
*/
public void printInnerClasses(){//throws ioexception
InnerClassData[] innerClasses = cls.getInnerClasses();
if(innerClasses != null){
if(innerClasses.length > 0){
out.print(" ");
out.println("InnerClass: ");
for(int i = 0 ; i < innerClasses.length; i++){
out.print(" ");
//access
String[] accflags = innerClasses[i].getAccess();
if(checkAccess(accflags)){
printAccess(accflags);
if (innerClasses[i].inner_name_index!=0) {
out.print("#"+innerClasses[i].inner_name_index+"= ");
}
out.print("#"+innerClasses[i].inner_class_info_index);
if (innerClasses[i].outer_class_info_index!=0) {
out.print(" of #"+innerClasses[i].outer_class_info_index);
}
out.print("; //");
if (innerClasses[i].inner_name_index!=0) {
out.print(cls.getName(innerClasses[i].inner_name_index)+"=");
}
PrintConstant(innerClasses[i].inner_class_info_index);
if (innerClasses[i].outer_class_info_index!=0) {
out.print(" of ");
PrintConstant(innerClasses[i].outer_class_info_index);
}
out.println();
}
}
}
}
}
/**
* Print constant pool information.
*/
public void printcp(){
int cpx = 1 ;
while (cpx < cls.getCpoolCount()) {
out.print("const #"+cpx+" = ");
cpx+=PrintlnConstantEntry(cpx);
}
out.println();
}
/**
* Print constant pool entry information.
*/
public int PrintlnConstantEntry(int cpx) {
int size=1;
byte tag=0;
try {
tag=cls.getTag(cpx);
} catch (IndexOutOfBoundsException e) {
out.println(" <Incorrect CP index>");
return 1;
}
out.print(cls.StringTag(cpx)+"\t");
Object x=cls.getCpoolEntryobj(cpx);
if (x==null) {
switch (tag) {
case CONSTANT_LONG:
case CONSTANT_DOUBLE:
size=2;
}
out.println("null;");
return size;
}
String str=cls.StringValue(cpx);
switch (tag) {
case CONSTANT_CLASS:
case CONSTANT_STRING:
out.println("#"+(((CPX)x).cpx)+";\t// "+str);
break;
case CONSTANT_FIELD:
case CONSTANT_METHOD:
case CONSTANT_INTERFACEMETHOD:
out.println("#"+((CPX2)x).cpx1+".#"+((CPX2)x).cpx2+";\t// "+str);
break;
case CONSTANT_NAMEANDTYPE:
out.println("#"+((CPX2)x).cpx1+":#"+((CPX2)x).cpx2+";// "+str);
break;
case CONSTANT_LONG:
case CONSTANT_DOUBLE:
size=2;
default:
out.println(str+";");
}
return size;
}
/**
* Checks access of class, field or method.
*/
public boolean checkAccess(String accflags[]){
boolean ispublic = false;
boolean isprotected = false;
boolean isprivate = false;
boolean ispackage = false;
for(int i= 0; i < accflags.length; i++){
if(accflags[i].equals("public")) ispublic = true;
else if (accflags[i].equals("protected")) isprotected = true;
else if (accflags[i].equals("private")) isprivate = true;
}
if(!(ispublic || isprotected || isprivate)) ispackage = true;
if((env.showAccess == env.PUBLIC) && (isprotected || isprivate || ispackage)) return false;
else if((env.showAccess == env.PROTECTED) && (isprivate || ispackage)) return false;
else if((env.showAccess == env.PACKAGE) && (isprivate)) return false;
else return true;
}
/**
* Prints access of class, field or method.
*/
public void printAccess(String []accflags){
for(int j = 0; j < accflags.length; j++){
out.print(accflags[j]+" ");
}
}
/**
* Print an integer so that it takes 'length' characters in
* the output. Temporary until formatting code is stable.
*/
public void printFixedWidthInt(long x, int length) {
CharArrayWriter baStream = new CharArrayWriter();
PrintWriter pStream = new PrintWriter(baStream);
pStream.print(x);
String str = baStream.toString();
for (int cnt = length - str.length(); cnt > 0; --cnt)
out.print(' ');
out.print(str);
}
protected int getbyte (int pc) {
return code[pc];
}
protected int getUbyte (int pc) {
return code[pc]&0xFF;
}
int getShort (int pc) {
return (code[pc]<<8) | (code[pc+1]&0xFF);
}
int getUShort (int pc) {
return ((code[pc]<<8) | (code[pc+1]&0xFF))&0xFFFF;
}
protected int getInt (int pc) {
return (getShort(pc)<<16) | (getShort(pc+2)&0xFFFF);
}
/**
* Print constant value at that index.
*/
void PrintConstant(int cpx) {
if (cpx==0) {
out.print("#0");
return;
}
byte tag=0;
try {
tag=cls.getTag(cpx);
} catch (IndexOutOfBoundsException e) {
out.print("#"+cpx);
return;
}
switch (tag) {
case CONSTANT_METHOD:
case CONSTANT_INTERFACEMETHOD:
case CONSTANT_FIELD: {
// CPX2 x=(CPX2)(cpool[cpx]);
CPX2 x = (CPX2)(cls.getCpoolEntry(cpx));
if (x.cpx1 == cls.getthis_cpx()) {
// don't print class part for local references
cpx=x.cpx2;
}
}
}
out.print(cls.TagString(tag)+" "+ cls.StringValue(cpx));
}
protected static int align (int n) {
return (n+3) & ~3 ;
}
public void printend(){
out.println("}");
out.println();
}
public String javaclassname(String name){
return name.replace('/','.');
}
/**
* Print attribute data in hex.
*/
public void printAttrData(AttrData attr){
byte []data = attr.getData();
int i = 0;
int j = 0;
out.print(" "+attr.getAttrName()+": ");
out.println("length = " + cls.toHex(attr.datalen));
out.print(" ");
while (i < data.length){
String databytestring = cls.toHex(data[i]);
if(databytestring.equals("0x")) out.print("00");
else if(databytestring.substring(2).length() == 1){
out.print("0"+databytestring.substring(2));
} else{
out.print(databytestring.substring(2));
}
j++;
if(j == 16) {
out.println();
out.print(" ");
j = 0;
}
else out.print(" ");
i++;
}
out.println();
}
}