/*
* Copyright 2004-2010 Brian S O'Neill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cojen.classfile;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.cojen.util.IntHashMap;
import org.cojen.classfile.attribute.Annotation;
import org.cojen.classfile.attribute.CodeAttr;
import org.cojen.classfile.attribute.LocalVariableTableAttr;
import org.cojen.classfile.attribute.SignatureAttr;
import org.cojen.classfile.attribute.StackMapTableAttr;
import org.cojen.classfile.constant.ConstantClassInfo;
import org.cojen.classfile.constant.ConstantDoubleInfo;
import org.cojen.classfile.constant.ConstantFieldInfo;
import org.cojen.classfile.constant.ConstantFloatInfo;
import org.cojen.classfile.constant.ConstantIntegerInfo;
import org.cojen.classfile.constant.ConstantInterfaceMethodInfo;
import org.cojen.classfile.constant.ConstantLongInfo;
import org.cojen.classfile.constant.ConstantMethodInfo;
import org.cojen.classfile.constant.ConstantNameAndTypeInfo;
import org.cojen.classfile.constant.ConstantStringInfo;
import org.cojen.classfile.constant.ConstantUTFInfo;
/**
* Disassembles a ClassFile into a pseudo Java assembly language format.
*
* @author Brian S O'Neill
*/
class AssemblyStylePrinter implements DisassemblyTool.Printer {
private ClassFile mClassFile;
private ConstantPool mCp;
private PrintWriter mOut;
private byte[] mByteCodes;
// Current address being decompiled.
private int mAddress;
// Maps int address keys to String labels.
private IntHashMap<Object> mLabels;
private ExceptionHandler[] mExceptionHandlers;
// Maps int catch locations to Lists of ExceptionHandler objects.
private IntHashMap<List<ExceptionHandler>> mCatchLocations;
public AssemblyStylePrinter() {
}
public void disassemble(ClassFile cf, PrintWriter out) {
disassemble(cf, out, "");
}
private void disassemble(ClassFile cf, PrintWriter out, String indent) {
mClassFile = cf;
mCp = cf.getConstantPool();
mOut = out;
if (indent.length() == 0 || mClassFile.getSourceFile() != null ||
mClassFile.isDeprecated() || mClassFile.isSynthetic()) {
println(indent, "/**");
boolean addBreak = false;
if (indent.length() == 0) {
print(indent, " * Disassembled on ");
print(new Date());
println(".");
addBreak = true;
}
if (indent.length() == 0 && mClassFile.getTarget() != null) {
if (addBreak) {
println(indent, " * ");
addBreak = false;
}
print(indent, " * @target ");
println(CodeAssemblerPrinter.escape(mClassFile.getTarget()));
}
if (mClassFile.getSourceFile() != null) {
if (addBreak) {
println(indent, " * ");
addBreak = false;
}
print(indent, " * @source ");
println(CodeAssemblerPrinter.escape(mClassFile.getSourceFile()));
}
if (mClassFile.isInnerClass()) {
if (addBreak) {
println(indent, " * ");
addBreak = false;
}
if (mClassFile.getInnerClassName() == null) {
println(indent, " * @anonymous");
} else {
print(indent, " * @name ");
println(CodeAssemblerPrinter.escape(mClassFile.getInnerClassName()));
}
}
if (mClassFile.isDeprecated()) {
if (addBreak) {
println(indent, " * ");
addBreak = false;
}
println(indent, " * @deprecated");
}
if (mClassFile.isSynthetic()) {
if (addBreak) {
println(indent, " * ");
addBreak = false;
}
println(indent, " * @synthetic");
}
// TODO: Just testing
SignatureAttr sig = mClassFile.getSignatureAttr();
if (sig != null) {
if (addBreak) {
println(indent, " * ");
addBreak = false;
}
println(indent, " * @signature " + sig.getSignature().getValue());
}
println(indent, " */");
}
disassemble(indent, mClassFile.getRuntimeVisibleAnnotations());
disassemble(indent, mClassFile.getRuntimeInvisibleAnnotations());
print(indent);
disassemble(mClassFile.getModifiers());
boolean isInterface = mClassFile.getModifiers().isInterface();
if (!isInterface) {
print("class ");
}
print(mClassFile.getClassName());
if (mClassFile.getSuperClassName() != null) {
print(" extends ");
print(mClassFile.getSuperClassName());
}
String innerIndent = indent + " ";
String[] interfaces = mClassFile.getInterfaces();
if (interfaces.length == 0) {
println(" {");
} else {
println();
for (int i=0; i<interfaces.length; i++) {
if (i == 0) {
print(innerIndent, "implements ");
} else {
println(",");
print(innerIndent, " ");
}
print(interfaces[i]);
}
println();
println(indent, "{");
}
FieldInfo[] fields = mClassFile.getFields();
MethodInfo[] methods = mClassFile.getMethods();
MethodInfo[] ctors = mClassFile.getConstructors();
MethodInfo init = mClassFile.getInitializer();
Object[] members = new Object[fields.length + methods.length +
ctors.length + ((init == null) ? 0 : 1)];
int m = 0;
for (int i=0; i<fields.length; i++) {
members[m++] = fields[i];
}
for (int i=0; i<methods.length; i++) {
members[m++] = methods[i];
}
for (int i=0; i<ctors.length; i++) {
members[m++] = ctors[i];
}
if (init != null) {
members[m++] = init;
}
sortMembers(members);
for (int i=0; i<members.length; i++) {
if (i > 0) {
println();
}
if (members[i] instanceof FieldInfo) {
disassemble(innerIndent, (FieldInfo)members[i]);
} else {
disassemble(innerIndent, (MethodInfo)members[i]);
}
}
mByteCodes = null;
mLabels = null;
mExceptionHandlers = null;
mCatchLocations = null;
ClassFile[] innerClasses = mClassFile.getInnerClasses();
for (int i=0; i<innerClasses.length; i++) {
if (i > 0 || members.length > 0) {
println();
}
AssemblyStylePrinter printer = new AssemblyStylePrinter();
printer.disassemble(innerClasses[i], mOut, innerIndent);
}
println(indent, "}");
mOut.flush();
mOut = null;
}
private void disassemble(String indent, FieldInfo field) {
SignatureAttr sig = field.getSignatureAttr();
if (field.isDeprecated() || field.isSynthetic() || sig != null) {
println(indent, "/**");
if (field.isDeprecated()) {
println(indent, " * @deprecated");
}
if (field.isSynthetic()) {
println(indent, " * @synthetic");
}
if (sig != null) {
println(indent, " * @signature " + sig.getSignature().getValue());
}
println(indent, " */");
}
disassemble(indent, field.getRuntimeVisibleAnnotations());
disassemble(indent, field.getRuntimeInvisibleAnnotations());
print(indent);
disassemble(field.getModifiers());
disassemble(field.getType());
print(" ");
print(field.getName());
ConstantInfo constant = field.getConstantValue();
if (constant != null) {
print(" = ");
disassemble(constant);
}
println(";");
}
private void disassemble(String indent, MethodInfo method) {
SignatureAttr sig = method.getSignatureAttr();
if (method.isDeprecated() || method.isSynthetic() || sig != null) {
println(indent, "/**");
if (method.isDeprecated()) {
println(indent, " * @deprecated");
}
if (method.isSynthetic()) {
println(indent, " * @synthetic");
}
if (sig != null) {
println(indent, " * @signature " + sig.getSignature().getValue());
}
println(indent, " */");
}
disassemble(indent, method.getRuntimeVisibleAnnotations());
disassemble(indent, method.getRuntimeInvisibleAnnotations());
print(indent);
MethodDesc md = method.getMethodDescriptor();
if ("<clinit>".equals(method.getName()) &&
md.getReturnType() == TypeDesc.VOID &&
md.getParameterCount() == 0 &&
(method.getModifiers().isStatic()) &&
(!method.getModifiers().isAbstract()) &&
method.getExceptions().length == 0) {
// Static initializer.
print("static");
} else {
Modifiers modifiers = method.getModifiers();
boolean varargs = modifiers.isVarArgs();
if (varargs) {
// Don't display the modifier.
modifiers = modifiers.toVarArgs(false);
}
disassemble(modifiers);
print(md.toMethodSignature(method.getName(), varargs));
}
CodeAttr code = method.getCodeAttr();
TypeDesc[] exceptions = method.getExceptions();
if (exceptions.length == 0) {
if (code == null) {
println(";");
} else {
println(" {");
}
} else {
println();
for (int i=0; i<exceptions.length; i++) {
if (i == 0) {
print(indent + " ", "throws ");
} else {
println(",");
print(indent + " ", " ");
}
print(exceptions[i].getFullName());
}
if (code == null) {
println(";");
} else {
println();
println(indent, "{");
}
}
if (code != null) {
disassemble(indent + " ", code);
println(indent, "}");
}
}
private void disassemble(Modifiers modifiers) {
print(modifiers);
if (modifiers.getBitmask() != 0) {
print(" ");
}
}
private void disassemble(ConstantInfo constant) {
disassemble(constant, false);
}
private void disassemble(ConstantInfo constant, boolean showClassLiteral) {
if (constant instanceof ConstantStringInfo) {
print("\"");
String value = ((ConstantStringInfo)constant).getValue();
print(CodeAssemblerPrinter.escape(value));
print("\"");
} else if (constant instanceof ConstantIntegerInfo) {
print(String.valueOf(((ConstantIntegerInfo)constant).getValue()));
} else if (constant instanceof ConstantLongInfo) {
print(String.valueOf(((ConstantLongInfo)constant).getValue()));
print("L");
} else if (constant instanceof ConstantFloatInfo) {
float value = ((ConstantFloatInfo)constant).getValue();
if (value != value) {
print("0.0f/0.0f");
} else if (value == Float.NEGATIVE_INFINITY) {
print("-1.0f/0.0f");
} else if (value == Float.POSITIVE_INFINITY) {
print("1.0f/0.0f");
} else {
print(String.valueOf(value));
print("f");
}
} else if (constant instanceof ConstantDoubleInfo) {
double value = ((ConstantDoubleInfo)constant).getValue();
if (value != value) {
print("0.0d/0.0d");
} else if (value == Float.NEGATIVE_INFINITY) {
print("-1.0d/0.0d");
} else if (value == Float.POSITIVE_INFINITY) {
print("1.0d/0.0d");
} else {
print(String.valueOf(value));
print("d");
}
} else if (constant instanceof ConstantClassInfo) {
ConstantClassInfo cci = (ConstantClassInfo)constant;
disassemble(cci.getType());
if (showClassLiteral) {
print(".class");
}
} else if (constant instanceof ConstantUTFInfo) {
print("\"");
String value = ((ConstantUTFInfo)constant).getValue();
print(CodeAssemblerPrinter.escape(value));
print("\"");
} else {
print(constant);
}
}
private void disassemble(TypeDesc type) {
print(type.getFullName());
}
private void disassemble(LocalVariable var) {
if (var != null) {
print(" // ");
print(var.getName());
print(": ");
disassemble(var.getType());
}
}
private void disassemble(String indent, Annotation[] annotations) {
for (int i=0; i<annotations.length; i++) {
print(indent);
disassemble(indent, annotations[i]);
println();
}
}
private void disassemble(String indent, Annotation ann) {
print("@");
print(ann.getType().getFullName());
Map mvMap = ann.getMemberValues();
if (mvMap.size() == 0) {
return;
}
print("(");
Iterator it = mvMap.entrySet().iterator();
int ordinal = 0;
while (it.hasNext()) {
if (ordinal++ > 0) {
print(", ");
}
Map.Entry entry = (Map.Entry)it.next();
String name = (String)entry.getKey();
if (!"value".equals(name)) {
print(name);
print("=");
}
disassemble(indent, (Annotation.MemberValue)entry.getValue());
}
print(")");
}
private void disassemble(String indent, Annotation.MemberValue mv) {
Object value = mv.getValue();
switch (mv.getTag()) {
default:
print("?");
break;
case Annotation.MEMBER_TAG_BOOLEAN:
ConstantIntegerInfo ci = (ConstantIntegerInfo)value;
print(ci.getValue() == 0 ? "false" : "true");
break;
case Annotation.MEMBER_TAG_BYTE:
case Annotation.MEMBER_TAG_SHORT:
case Annotation.MEMBER_TAG_INT:
case Annotation.MEMBER_TAG_LONG:
case Annotation.MEMBER_TAG_FLOAT:
case Annotation.MEMBER_TAG_DOUBLE:
case Annotation.MEMBER_TAG_STRING:
case Annotation.MEMBER_TAG_CLASS:
disassemble((ConstantInfo)value, true);
break;
case Annotation.MEMBER_TAG_CHAR: {
print("'");
char c = (char) ((ConstantIntegerInfo)value).getValue();
print(CodeAssemblerPrinter.escape(String.valueOf(c), true));
print("'");
break;
}
case Annotation.MEMBER_TAG_ENUM:
Annotation.EnumConstValue ecv = (Annotation.EnumConstValue)value;
print(TypeDesc.forDescriptor(ecv.getTypeName().getValue()).getFullName());
print(".");
print(ecv.getConstName().getValue());
break;
case Annotation.MEMBER_TAG_ANNOTATION:
disassemble(indent, (Annotation)value);
break;
case Annotation.MEMBER_TAG_ARRAY:
Annotation.MemberValue[] mvs = (Annotation.MemberValue[])value;
String originalIndent = indent;
boolean multiLine = false;
if (mvs.length > 0) {
if (mvs.length > 4 ||
(mvs.length > 1 && mvs[0].getTag() == Annotation.MEMBER_TAG_ENUM) ||
mvs[0].getTag() == Annotation.MEMBER_TAG_ARRAY ||
mvs[0].getTag() == Annotation.MEMBER_TAG_ANNOTATION) {
multiLine = true;
indent = indent + " ";
}
}
if (multiLine || mvs.length != 1) {
print("{");
if (multiLine) {
println();
}
}
for (int j=0; j<mvs.length; j++) {
if (multiLine) {
print(indent);
}
disassemble(indent, mvs[j]);
if (j + 1 < mvs.length) {
print(",");
if (!multiLine) {
print(" ");
}
}
if (multiLine) {
println();
}
}
indent = originalIndent;
if (multiLine || mvs.length != 1) {
if (multiLine) {
print(indent);
}
print("}");
}
break;
}
}
private void disassemble(String indent, CodeAttr code) {
CodeBuffer buffer = code.getCodeBuffer();
mExceptionHandlers = buffer.getExceptionHandlers();
print(indent);
print("// max stack: ");
println(String.valueOf(buffer.getMaxStackDepth()));
print(indent);
print("// max locals: ");
println(String.valueOf(buffer.getMaxLocals()));
mByteCodes = buffer.getByteCodes();
gatherLabels();
Location currentLoc = new Location() {
public int getLocation() {
return mAddress;
}
public int compareTo(Location other) {
if (this == other) {
return 0;
}
int loca = getLocation();
int locb = other.getLocation();
if (loca < locb) {
return -1;
} else if (loca > locb) {
return 1;
} else {
return 0;
}
}
};
StackMapTableAttr.StackMapFrame frame;
if (code.getStackMapTable() == null) {
frame = null;
} else {
frame = code.getStackMapTable().getInitialFrame();
}
int currentLine = -1;
for (mAddress = 0; mAddress < mByteCodes.length; mAddress++) {
int nextLine = code.getLineNumber(currentLoc);
if (nextLine != currentLine) {
if ((currentLine = nextLine) >= 0) {
println(indent, "// line " + currentLine);
}
}
// Check if a label needs to be created and/or located.
locateLabel(indent);
frame = stackMap(indent, frame);
byte opcode = mByteCodes[mAddress];
String mnemonic;
try {
mnemonic = Opcode.getMnemonic(opcode);
} catch (IllegalArgumentException e) {
mnemonic = String.valueOf(opcode & 0xff);
}
print(indent, mnemonic);
switch (opcode) {
default:
break;
// Opcodes with no operands...
case Opcode.NOP:
case Opcode.BREAKPOINT:
case Opcode.ACONST_NULL:
case Opcode.ICONST_M1:
case Opcode.ICONST_0:
case Opcode.ICONST_1:
case Opcode.ICONST_2:
case Opcode.ICONST_3:
case Opcode.ICONST_4:
case Opcode.ICONST_5:
case Opcode.LCONST_0:
case Opcode.LCONST_1:
case Opcode.FCONST_0:
case Opcode.FCONST_1:
case Opcode.FCONST_2:
case Opcode.DCONST_0:
case Opcode.DCONST_1:
case Opcode.POP:
case Opcode.POP2:
case Opcode.DUP:
case Opcode.DUP_X1:
case Opcode.DUP_X2:
case Opcode.DUP2:
case Opcode.DUP2_X1:
case Opcode.DUP2_X2:
case Opcode.SWAP:
case Opcode.IADD: case Opcode.LADD:
case Opcode.FADD: case Opcode.DADD:
case Opcode.ISUB: case Opcode.LSUB:
case Opcode.FSUB: case Opcode.DSUB:
case Opcode.IMUL: case Opcode.LMUL:
case Opcode.FMUL: case Opcode.DMUL:
case Opcode.IDIV: case Opcode.LDIV:
case Opcode.FDIV: case Opcode.DDIV:
case Opcode.IREM: case Opcode.LREM:
case Opcode.FREM: case Opcode.DREM:
case Opcode.INEG: case Opcode.LNEG:
case Opcode.FNEG: case Opcode.DNEG:
case Opcode.ISHL: case Opcode.LSHL:
case Opcode.ISHR: case Opcode.LSHR:
case Opcode.IUSHR: case Opcode.LUSHR:
case Opcode.IAND: case Opcode.LAND:
case Opcode.IOR: case Opcode.LOR:
case Opcode.IXOR: case Opcode.LXOR:
case Opcode.FCMPL: case Opcode.DCMPL:
case Opcode.FCMPG: case Opcode.DCMPG:
case Opcode.LCMP:
case Opcode.I2L:
case Opcode.I2F:
case Opcode.I2D:
case Opcode.L2I:
case Opcode.L2F:
case Opcode.L2D:
case Opcode.F2I:
case Opcode.F2L:
case Opcode.F2D:
case Opcode.D2I:
case Opcode.D2L:
case Opcode.D2F:
case Opcode.I2B:
case Opcode.I2C:
case Opcode.I2S:
case Opcode.IRETURN:
case Opcode.LRETURN:
case Opcode.FRETURN:
case Opcode.DRETURN:
case Opcode.ARETURN:
case Opcode.RETURN:
case Opcode.IALOAD:
case Opcode.LALOAD:
case Opcode.FALOAD:
case Opcode.DALOAD:
case Opcode.AALOAD:
case Opcode.BALOAD:
case Opcode.CALOAD:
case Opcode.SALOAD:
case Opcode.IASTORE:
case Opcode.LASTORE:
case Opcode.FASTORE:
case Opcode.DASTORE:
case Opcode.AASTORE:
case Opcode.BASTORE:
case Opcode.CASTORE:
case Opcode.SASTORE:
case Opcode.ARRAYLENGTH:
case Opcode.ATHROW:
case Opcode.MONITORENTER:
case Opcode.MONITOREXIT:
println();
continue;
case Opcode.ILOAD_0:
case Opcode.LLOAD_0:
case Opcode.FLOAD_0:
case Opcode.DLOAD_0:
case Opcode.ALOAD_0:
disassemble(code.getLocalVariable(mAddress, 0));
println();
continue;
case Opcode.ISTORE_0:
case Opcode.LSTORE_0:
case Opcode.FSTORE_0:
case Opcode.DSTORE_0:
case Opcode.ASTORE_0:
disassemble(code.getLocalVariable(mAddress + 1, 0));
println();
continue;
case Opcode.ILOAD_1:
case Opcode.LLOAD_1:
case Opcode.FLOAD_1:
case Opcode.DLOAD_1:
case Opcode.ALOAD_1:
disassemble(code.getLocalVariable(mAddress, 1));
println();
continue;
case Opcode.ISTORE_1:
case Opcode.LSTORE_1:
case Opcode.FSTORE_1:
case Opcode.DSTORE_1:
case Opcode.ASTORE_1:
disassemble(code.getLocalVariable(mAddress + 1, 1));
println();
continue;
case Opcode.ILOAD_2:
case Opcode.LLOAD_2:
case Opcode.FLOAD_2:
case Opcode.DLOAD_2:
case Opcode.ALOAD_2:
disassemble(code.getLocalVariable(mAddress, 2));
println();
continue;
case Opcode.ISTORE_2:
case Opcode.LSTORE_2:
case Opcode.FSTORE_2:
case Opcode.DSTORE_2:
case Opcode.ASTORE_2:
disassemble(code.getLocalVariable(mAddress + 1, 2));
println();
continue;
case Opcode.ILOAD_3:
case Opcode.LLOAD_3:
case Opcode.FLOAD_3:
case Opcode.DLOAD_3:
case Opcode.ALOAD_3:
disassemble(code.getLocalVariable(mAddress, 3));
println();
continue;
case Opcode.ISTORE_3:
case Opcode.LSTORE_3:
case Opcode.FSTORE_3:
case Opcode.DSTORE_3:
case Opcode.ASTORE_3:
disassemble(code.getLocalVariable(mAddress + 1, 3));
println();
continue;
// End opcodes with no operands.
}
// Space to separate operands.
print(" ");
int index;
ConstantInfo constant;
switch (opcode) {
default:
break;
// Opcodes that load a constant from the constant pool...
case Opcode.LDC:
case Opcode.LDC_W:
case Opcode.LDC2_W:
switch (opcode) {
case Opcode.LDC:
index = readUnsignedByte();
break;
case Opcode.LDC_W:
case Opcode.LDC2_W:
index = readUnsignedShort();
break;
default:
index = 0;
break;
}
disassemble(getConstant(index), true);
break;
case Opcode.NEW:
case Opcode.ANEWARRAY:
case Opcode.CHECKCAST:
case Opcode.INSTANCEOF:
constant = getConstant(readUnsignedShort());
if (constant instanceof ConstantClassInfo) {
disassemble(constant);
} else {
print(constant);
}
break;
case Opcode.MULTIANEWARRAY:
constant = getConstant(readUnsignedShort());
int dims = readUnsignedByte();
if (constant instanceof ConstantClassInfo) {
disassemble(constant);
} else {
print(constant);
}
print(" ");
print(String.valueOf(dims));
break;
case Opcode.GETSTATIC:
case Opcode.PUTSTATIC:
case Opcode.GETFIELD:
case Opcode.PUTFIELD:
constant = getConstant(readUnsignedShort());
if (constant instanceof ConstantFieldInfo) {
ConstantFieldInfo field = (ConstantFieldInfo)constant;
Descriptor type = field.getNameAndType().getType();
if (type instanceof TypeDesc) {
disassemble((TypeDesc)type);
} else {
print(type);
}
print(" ");
print(field.getParentClass().getType().getFullName());
print(".");
print(field.getNameAndType().getName());
} else {
print(constant);
}
break;
case Opcode.INVOKEVIRTUAL:
case Opcode.INVOKESPECIAL:
case Opcode.INVOKESTATIC:
case Opcode.INVOKEINTERFACE:
case Opcode.INVOKEDYNAMIC:
constant = getConstant(readUnsignedShort());
String className;
ConstantNameAndTypeInfo nameAndType;
if (opcode == Opcode.INVOKEINTERFACE) {
// Read and ignore nargs and padding byte.
readShort();
if (!(constant instanceof ConstantInterfaceMethodInfo)) {
print(constant);
break;
}
ConstantInterfaceMethodInfo method =
(ConstantInterfaceMethodInfo)constant;
className =
method.getParentClass().getType().getFullName();
nameAndType = method.getNameAndType();
} else if (opcode == Opcode.INVOKEDYNAMIC) {
// Read and ignore extra bytes.
readShort();
className = null;
nameAndType = (ConstantNameAndTypeInfo)constant;
} else {
if (!(constant instanceof ConstantMethodInfo)) {
print(constant);
break;
}
ConstantMethodInfo method = (ConstantMethodInfo)constant;
className =
method.getParentClass().getType().getFullName();
nameAndType = method.getNameAndType();
}
Descriptor type = nameAndType.getType();
if (!(type instanceof MethodDesc)) {
print(type);
break;
}
disassemble(((MethodDesc)type).getReturnType());
print(" ");
if (className != null) {
print(className);
print(".");
}
print(nameAndType.getName());
print("(");
TypeDesc[] params = ((MethodDesc)type).getParameterTypes();
for (int i=0; i<params.length; i++) {
if (i > 0) {
print(", ");
}
disassemble(params[i]);
}
print(")");
break;
// End opcodes that load a constant from the constant pool.
// Opcodes that load or store local variables...
case Opcode.ILOAD:
case Opcode.LLOAD:
case Opcode.FLOAD:
case Opcode.DLOAD:
case Opcode.ALOAD:
case Opcode.RET:
int varNum = readUnsignedByte();
print(String.valueOf(varNum));
disassemble(code.getLocalVariable(mAddress, varNum));
break;
case Opcode.ISTORE:
case Opcode.LSTORE:
case Opcode.FSTORE:
case Opcode.DSTORE:
case Opcode.ASTORE:
varNum = readUnsignedByte();
print(String.valueOf(varNum));
disassemble(code.getLocalVariable(mAddress + 1, varNum));
break;
case Opcode.IINC:
print(String.valueOf(varNum = readUnsignedByte()));
print(" ");
int incValue = readByte();
if (incValue >= 0) {
print("+");
}
print(String.valueOf(incValue));
disassemble(code.getLocalVariable(mAddress, varNum));
break;
// End opcodes that load or store local variables.
// Opcodes that branch to another address.
case Opcode.GOTO:
case Opcode.JSR:
case Opcode.IFNULL:
case Opcode.IFNONNULL:
case Opcode.IF_ACMPEQ:
case Opcode.IF_ACMPNE:
case Opcode.IFEQ:
case Opcode.IFNE:
case Opcode.IFLT:
case Opcode.IFGE:
case Opcode.IFGT:
case Opcode.IFLE:
case Opcode.IF_ICMPEQ:
case Opcode.IF_ICMPNE:
case Opcode.IF_ICMPLT:
case Opcode.IF_ICMPGE:
case Opcode.IF_ICMPGT:
case Opcode.IF_ICMPLE:
print(getLabel(mAddress + readShort()));
break;
case Opcode.GOTO_W:
case Opcode.JSR_W:
print(getLabel(mAddress + readInt()));
break;
// End opcodes that branch to another address.
// Miscellaneous opcodes...
case Opcode.BIPUSH:
int value = readByte();
print(String.valueOf(value));
printCharLiteral(value);
break;
case Opcode.SIPUSH:
value = readShort();
print(String.valueOf(value));
printCharLiteral(value);
break;
case Opcode.NEWARRAY:
int atype = readByte();
switch (atype) {
case 4: // T_BOOLEAN
print("boolean");
break;
case 5: // T_CHAR
print("char");
break;
case 6: // T_FLOAT
print("float");
break;
case 7: // T_DOUBLE
print("double");
break;
case 8: // T_BYTE
print("byte");
break;
case 9: // T_SHORT
print("short");
break;
case 10: // T_INT
print("int");
break;
case 11: // T_LONG
print("long");
break;
default:
print("T_" + atype);
break;
}
break;
case Opcode.TABLESWITCH:
case Opcode.LOOKUPSWITCH:
int opcodeAddress = mAddress;
// Read padding until address is 32 bit word aligned.
while (((mAddress + 1) & 3) != 0) {
++mAddress;
}
String defaultLocation = getLabel(opcodeAddress + readInt());
int[] cases;
String[] locations;
if (opcode == Opcode.TABLESWITCH) {
int lowValue = readInt();
int highValue = readInt();
int caseCount = highValue - lowValue + 1;
print("// " + caseCount + " cases");
try {
cases = new int[caseCount];
} catch (NegativeArraySizeException e) {
break;
}
locations = new String[caseCount];
for (int i=0; i<caseCount; i++) {
cases[i] = lowValue + i;
locations[i] = getLabel(opcodeAddress + readInt());
}
} else {
int caseCount = readInt();
print("// " + caseCount + " cases");
try {
cases = new int[caseCount];
} catch (NegativeArraySizeException e) {
break;
}
locations = new String[caseCount];
for (int i=0; i<caseCount; i++) {
cases[i] = readInt();
locations[i] = getLabel(opcodeAddress + readInt());
}
}
println();
print(indent, " default: goto ");
println(defaultLocation);
String prefix = indent + " " + "case ";
for (int i=0; i<cases.length; i++) {
print(prefix + cases[i]);
print(": goto ");
print(locations[i]);
printCharLiteral(cases[i]);
println();
}
break;
case Opcode.WIDE:
opcode = mByteCodes[++mAddress];
print(Opcode.getMnemonic(opcode));
print(" ");
switch (opcode) {
default:
break;
case Opcode.ILOAD: case Opcode.ISTORE:
case Opcode.LLOAD: case Opcode.LSTORE:
case Opcode.FLOAD: case Opcode.FSTORE:
case Opcode.DLOAD: case Opcode.DSTORE:
case Opcode.ALOAD: case Opcode.ASTORE:
case Opcode.RET:
print(String.valueOf(readUnsignedShort()));
break;
case Opcode.IINC:
print(String.valueOf(readUnsignedShort()));
print(" ");
incValue = readShort();
if (incValue >= 0) {
print("+");
}
print(String.valueOf(incValue));
break;
}
break;
} // end huge switch
println();
} // end for loop
}
private void gatherLabels() {
mLabels = new IntHashMap<Object>();
mCatchLocations = new IntHashMap<List<ExceptionHandler>>
(mExceptionHandlers.length * 2 + 1);
// Gather labels for any exception handlers.
for (int i = mExceptionHandlers.length - 1; i >= 0; i--) {
ExceptionHandler handler = mExceptionHandlers[i];
createLabel(new Integer(handler.getStartLocation().getLocation()));
createLabel(new Integer(handler.getEndLocation().getLocation()));
int labelKey = handler.getCatchLocation().getLocation();
createLabel(labelKey);
List<ExceptionHandler> list = mCatchLocations.get(labelKey);
if (list == null) {
list = new ArrayList<ExceptionHandler>(2);
mCatchLocations.put(labelKey, list);
}
list.add(handler);
}
// Now gather labels that occur within byte code.
for (mAddress = 0; mAddress < mByteCodes.length; mAddress++) {
byte opcode = mByteCodes[mAddress];
switch (opcode) {
default:
break;
// Opcodes that use labels.
case Opcode.GOTO:
case Opcode.JSR:
case Opcode.IFNULL:
case Opcode.IFNONNULL:
case Opcode.IF_ACMPEQ:
case Opcode.IF_ACMPNE:
case Opcode.IFEQ:
case Opcode.IFNE:
case Opcode.IFLT:
case Opcode.IFGE:
case Opcode.IFGT:
case Opcode.IFLE:
case Opcode.IF_ICMPEQ:
case Opcode.IF_ICMPNE:
case Opcode.IF_ICMPLT:
case Opcode.IF_ICMPGE:
case Opcode.IF_ICMPGT:
case Opcode.IF_ICMPLE:
createLabel(new Integer(mAddress + readShort()));
break;
case Opcode.GOTO_W:
case Opcode.JSR_W:
createLabel(new Integer(mAddress + readInt()));
break;
case Opcode.TABLESWITCH:
case Opcode.LOOKUPSWITCH:
int opcodeAddress = mAddress;
// Read padding until address is 32 bit word aligned.
while (((mAddress + 1) & 3) != 0) {
++mAddress;
}
// Read the default location.
createLabel(new Integer(opcodeAddress + readInt()));
if (opcode == Opcode.TABLESWITCH) {
int lowValue = readInt();
int highValue = readInt();
int caseCount = highValue - lowValue + 1;
for (int i=0; i<caseCount; i++) {
// Read the branch location.
createLabel(new Integer(opcodeAddress + readInt()));
}
} else {
int caseCount = readInt();
for (int i=0; i<caseCount; i++) {
// Skip the case value.
mAddress += 4;
// Read the branch location.
createLabel(new Integer(opcodeAddress + readInt()));
}
}
break;
// All other operations are skipped. The amount to skip
// depends on the operand size.
// Opcodes with no operands...
case Opcode.NOP:
case Opcode.BREAKPOINT:
case Opcode.ACONST_NULL:
case Opcode.ICONST_M1:
case Opcode.ICONST_0:
case Opcode.ICONST_1:
case Opcode.ICONST_2:
case Opcode.ICONST_3:
case Opcode.ICONST_4:
case Opcode.ICONST_5:
case Opcode.LCONST_0:
case Opcode.LCONST_1:
case Opcode.FCONST_0:
case Opcode.FCONST_1:
case Opcode.FCONST_2:
case Opcode.DCONST_0:
case Opcode.DCONST_1:
case Opcode.POP:
case Opcode.POP2:
case Opcode.DUP:
case Opcode.DUP_X1:
case Opcode.DUP_X2:
case Opcode.DUP2:
case Opcode.DUP2_X1:
case Opcode.DUP2_X2:
case Opcode.SWAP:
case Opcode.IADD: case Opcode.LADD:
case Opcode.FADD: case Opcode.DADD:
case Opcode.ISUB: case Opcode.LSUB:
case Opcode.FSUB: case Opcode.DSUB:
case Opcode.IMUL: case Opcode.LMUL:
case Opcode.FMUL: case Opcode.DMUL:
case Opcode.IDIV: case Opcode.LDIV:
case Opcode.FDIV: case Opcode.DDIV:
case Opcode.IREM: case Opcode.LREM:
case Opcode.FREM: case Opcode.DREM:
case Opcode.INEG: case Opcode.LNEG:
case Opcode.FNEG: case Opcode.DNEG:
case Opcode.ISHL: case Opcode.LSHL:
case Opcode.ISHR: case Opcode.LSHR:
case Opcode.IUSHR: case Opcode.LUSHR:
case Opcode.IAND: case Opcode.LAND:
case Opcode.IOR: case Opcode.LOR:
case Opcode.IXOR: case Opcode.LXOR:
case Opcode.FCMPL: case Opcode.DCMPL:
case Opcode.FCMPG: case Opcode.DCMPG:
case Opcode.LCMP:
case Opcode.I2L:
case Opcode.I2F:
case Opcode.I2D:
case Opcode.L2I:
case Opcode.L2F:
case Opcode.L2D:
case Opcode.F2I:
case Opcode.F2L:
case Opcode.F2D:
case Opcode.D2I:
case Opcode.D2L:
case Opcode.D2F:
case Opcode.I2B:
case Opcode.I2C:
case Opcode.I2S:
case Opcode.IRETURN:
case Opcode.LRETURN:
case Opcode.FRETURN:
case Opcode.DRETURN:
case Opcode.ARETURN:
case Opcode.RETURN:
case Opcode.IALOAD:
case Opcode.LALOAD:
case Opcode.FALOAD:
case Opcode.DALOAD:
case Opcode.AALOAD:
case Opcode.BALOAD:
case Opcode.CALOAD:
case Opcode.SALOAD:
case Opcode.IASTORE:
case Opcode.LASTORE:
case Opcode.FASTORE:
case Opcode.DASTORE:
case Opcode.AASTORE:
case Opcode.BASTORE:
case Opcode.CASTORE:
case Opcode.SASTORE:
case Opcode.ARRAYLENGTH:
case Opcode.ATHROW:
case Opcode.MONITORENTER:
case Opcode.MONITOREXIT:
case Opcode.ILOAD_0: case Opcode.ISTORE_0:
case Opcode.ILOAD_1: case Opcode.ISTORE_1:
case Opcode.ILOAD_2: case Opcode.ISTORE_2:
case Opcode.ILOAD_3: case Opcode.ISTORE_3:
case Opcode.LLOAD_0: case Opcode.LSTORE_0:
case Opcode.LLOAD_1: case Opcode.LSTORE_1:
case Opcode.LLOAD_2: case Opcode.LSTORE_2:
case Opcode.LLOAD_3: case Opcode.LSTORE_3:
case Opcode.FLOAD_0: case Opcode.FSTORE_0:
case Opcode.FLOAD_1: case Opcode.FSTORE_1:
case Opcode.FLOAD_2: case Opcode.FSTORE_2:
case Opcode.FLOAD_3: case Opcode.FSTORE_3:
case Opcode.DLOAD_0: case Opcode.DSTORE_0:
case Opcode.DLOAD_1: case Opcode.DSTORE_1:
case Opcode.DLOAD_2: case Opcode.DSTORE_2:
case Opcode.DLOAD_3: case Opcode.DSTORE_3:
case Opcode.ALOAD_0: case Opcode.ASTORE_0:
case Opcode.ALOAD_1: case Opcode.ASTORE_1:
case Opcode.ALOAD_2: case Opcode.ASTORE_2:
case Opcode.ALOAD_3: case Opcode.ASTORE_3:
break;
// Opcodes with one operand byte...
case Opcode.LDC:
case Opcode.ILOAD: case Opcode.ISTORE:
case Opcode.LLOAD: case Opcode.LSTORE:
case Opcode.FLOAD: case Opcode.FSTORE:
case Opcode.DLOAD: case Opcode.DSTORE:
case Opcode.ALOAD: case Opcode.ASTORE:
case Opcode.RET:
case Opcode.IINC:
case Opcode.BIPUSH:
case Opcode.NEWARRAY:
mAddress += 1;
break;
// Opcodes with two operand bytes...
case Opcode.LDC_W:
case Opcode.LDC2_W:
case Opcode.NEW:
case Opcode.ANEWARRAY:
case Opcode.CHECKCAST:
case Opcode.INSTANCEOF:
case Opcode.GETSTATIC:
case Opcode.PUTSTATIC:
case Opcode.GETFIELD:
case Opcode.PUTFIELD:
case Opcode.INVOKEVIRTUAL:
case Opcode.INVOKESPECIAL:
case Opcode.INVOKESTATIC:
case Opcode.SIPUSH:
mAddress += 2;
break;
// Opcodes with three operand bytes...
case Opcode.MULTIANEWARRAY:
mAddress += 3;
break;
// Opcodes with four operand bytes...
case Opcode.INVOKEINTERFACE:
case Opcode.INVOKEDYNAMIC:
mAddress += 4;
break;
// Wide opcode has a variable sized operand.
case Opcode.WIDE:
opcode = mByteCodes[++mAddress];
mAddress += 2;
if (opcode == Opcode.IINC) {
mAddress += 2;
}
break;
} // end huge switch
} // end for loop
Integer[] keys = new Integer[mLabels.size()];
mLabels.keySet().toArray(keys);
Arrays.sort(keys);
for (int i=0; i<keys.length; i++) {
mLabels.put(keys[i], "L" + (i + 1) + '_' + keys[i]);
}
}
private void createLabel(int labelKey) {
mLabels.put(labelKey, (Object) labelKey);
}
private ConstantInfo getConstant(int index) {
try {
return mCp.getConstant(index);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
private String getLabel(int address) {
Object label = mLabels.get(new Integer(address));
if (label == null || (!(label instanceof String))) {
return "L?_" + address;
} else {
return (String)label;
}
}
private void locateLabel(String indent) {
int labelKey = mAddress;
Object labelValue = mLabels.get(labelKey);
if (labelValue == null) {
return;
}
int len = indent.length() - 4;
if (len > 0) {
print(indent.substring(0, len));
}
print(labelValue);
println(":");
List<ExceptionHandler> handlers = mCatchLocations.get(labelKey);
if (handlers != null) {
for (int i=0; i<handlers.size(); i++) {
ExceptionHandler handler = handlers.get(i);
print(indent, "try (");
print(getLabel(handler.getStartLocation().getLocation()));
print("..");
print(getLabel(handler.getEndLocation().getLocation()));
print(") catch (");
if (handler.getCatchType() == null) {
print("...");
} else {
disassemble(handler.getCatchType());
}
println(")");
}
}
}
private StackMapTableAttr.StackMapFrame stackMap(String indent,
StackMapTableAttr.StackMapFrame frame)
{
if (frame == null) {
return null;
}
if (mAddress < frame.getOffset()) {
return frame;
}
print(indent);
print("// stack: ");
print(frame.getStackItemInfos());
println();
print(indent);
print("// locals: ");
print(frame.getLocalInfos());
println();
return frame.getNext();
}
private void print(StackMapTableAttr.VerificationTypeInfo[] infos) {
print('{');
int num = 0;
for (int i=0; i<infos.length; i++) {
if (i > 0) {
print(", ");
}
print(num);
print('=');
StackMapTableAttr.VerificationTypeInfo info = infos[i];
print(info.toString());
if (info.getType() == null || !info.getType().isDoubleWord()) {
num += 1;
} else {
num += 2;
}
}
print('}');
}
private int readByte() {
return mByteCodes[++mAddress];
}
private int readUnsignedByte() {
return mByteCodes[++mAddress] & 0xff;
}
private int readShort() {
return (mByteCodes[++mAddress] << 8) | (mByteCodes[++mAddress] & 0xff);
}
private int readUnsignedShort() {
return
((mByteCodes[++mAddress] & 0xff) << 8) |
((mByteCodes[++mAddress] & 0xff) << 0);
}
private int readInt() {
return
(mByteCodes[++mAddress] << 24) |
((mByteCodes[++mAddress] & 0xff) << 16) |
((mByteCodes[++mAddress] & 0xff) << 8) |
((mByteCodes[++mAddress] & 0xff) << 0);
}
private void print(Object text) {
mOut.print(text);
}
private void println(Object text) {
mOut.println(text);
}
private void print(String indent, Object text) {
mOut.print(indent);
mOut.print(text);
}
private void println(String indent, Object text) {
mOut.print(indent);
mOut.println(text);
}
private void println() {
mOut.println();
}
private void printCharLiteral(int value) {
if (value >= 0 && value <= 65535) {
int type = Character.getType((char)value);
switch (type) {
case Character.UPPERCASE_LETTER:
case Character.LOWERCASE_LETTER:
case Character.TITLECASE_LETTER:
case Character.MODIFIER_LETTER:
case Character.OTHER_LETTER:
case Character.NON_SPACING_MARK:
case Character.ENCLOSING_MARK:
case Character.COMBINING_SPACING_MARK:
case Character.DECIMAL_DIGIT_NUMBER:
case Character.LETTER_NUMBER:
case Character.OTHER_NUMBER:
case Character.DASH_PUNCTUATION:
case Character.START_PUNCTUATION:
case Character.END_PUNCTUATION:
case Character.CONNECTOR_PUNCTUATION:
case Character.OTHER_PUNCTUATION:
case Character.MATH_SYMBOL:
case Character.CURRENCY_SYMBOL:
case Character.MODIFIER_SYMBOL:
case Character.OTHER_SYMBOL:
print(" // '");
print(String.valueOf((char)value));
print("'");
}
}
}
private void sortMembers(Object[] members) {
Arrays.sort(members, new MemberComparator());
}
/**
* Orders members in this canonical sequence:
*
* - statics
* - fields
* - initializer
* - methods
* - non-statics
* - fields
* - constructors
* - methods
*
* Fields, constructors, and methods are sorted:
* - public
* - final
* - non-final
* - transient
* - protected
* - final
* - non-final
* - transient
* - package
* - final
* - non-final
* - transient
* - private
* - final
* - non-final
* - transient
*/
private class MemberComparator implements Comparator<Object> {
public int compare(Object a, Object b) {
Modifiers aFlags, bFlags;
if (a instanceof FieldInfo) {
aFlags = ((FieldInfo)a).getModifiers();
} else {
aFlags = ((MethodInfo)a).getModifiers();
}
if (b instanceof FieldInfo) {
bFlags = ((FieldInfo)b).getModifiers();
} else {
bFlags = ((MethodInfo)b).getModifiers();
}
// static before non-static
if (aFlags.isStatic()) {
if (!bFlags.isStatic()) {
return -1;
}
} else {
if (bFlags.isStatic()) {
return 1;
}
}
// fields before methods
if (a instanceof FieldInfo) {
if (b instanceof MethodInfo) {
return -1;
}
} else {
if (!(b instanceof MethodInfo)) {
return 1;
}
// initializers and constructors before regular methods
String aName = ((MethodInfo)a).getName();
String bName = ((MethodInfo)b).getName();
if ("<init>".equals(aName) || "<clinit>".equals(aName)) {
if ("<init>".equals(bName) || "<clinit>".equals(bName)) {
} else {
return -1;
}
} else {
if ("<init>".equals(bName) || "<clinit>".equals(bName)) {
return 1;
}
}
}
// public, protected, package, private order
int aValue, bValue;
if (aFlags.isPublic()) {
aValue = 0;
} else if (aFlags.isProtected()) {
aValue = 4;
} else if (!aFlags.isPrivate()) {
aValue = 8;
} else {
aValue = 12;
}
if (bFlags.isPublic()) {
bValue = 0;
} else if (bFlags.isProtected()) {
bValue = 4;
} else if (!bFlags.isPrivate()) {
bValue = 8;
} else {
bValue = 12;
}
// final before non-final
aValue += (aFlags.isFinal()) ? 0 : 2;
bValue += (bFlags.isFinal()) ? 0 : 2;
// transient after non-transient
aValue += (aFlags.isTransient()) ? 1 : 0;
bValue += (bFlags.isTransient()) ? 1 : 0;
if (aValue < bValue) {
return -1;
} else if (aValue > bValue) {
return 1;
}
return 0;
}
}
}