package org.apache.bcel.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.BitSet;
import java.util.function.Consumer;
import org.apache.bcel.Const;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.ConstantFieldref;
import org.apache.bcel.classfile.ConstantInterfaceMethodref;
import org.apache.bcel.classfile.ConstantInvokeDynamic;
import org.apache.bcel.classfile.ConstantMethodref;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Utility;
import org.apache.bcel.util.ByteSequence;
import org.apache.bcel.util.Class2HTML;
import org.apache.bcel.util.ConstantHTML;
final class CodeHTML {
private static boolean wide;
private final String className;
private final PrintWriter printWriter;
private BitSet gotoSet;
private final ConstantPool constantPool;
private final ConstantHTML constantHtml;
CodeHTML(String dir, String className, Method[] methods, ConstantPool constantPool, ConstantHTML constantHtml, Charset charset) throws IOException {
this.className = className;
this.constantPool = constantPool;
this.constantHtml = constantHtml;
PrintWriter newPrintWriter = new PrintWriter(dir + className + "_code.html", charset.name());
Throwable var8 = null;
try {
this.printWriter = newPrintWriter;
this.printWriter.print("
");
this.printWriter.println("");
for(int i = 0; i < methods.length; ++i) {
this.writeMethod(methods[i], i);
}
this.printWriter.println("");
} catch (Throwable var17) {
var8 = var17;
throw var17;
} finally {
if(newPrintWriter != null) {
if(var8 != null) {
try {
newPrintWriter.close();
} catch (Throwable var16) {
var8.addSuppressed(var16);
}
} else {
newPrintWriter.close();
}
}
}
}
private String codeToHTML(ByteSequence bytes, int methodNumber) throws IOException {
short opcode = (short)bytes.readUnsignedByte();
int defaultOffset = 0;
int noPadBytes = 0;
StringBuilder buf = new StringBuilder(256);
buf.append("").append(Const.getOpcodeName(opcode)).append("");
int npairs;
int windex;
if(opcode == 170 || opcode == 171) {
npairs = bytes.getIndex() % 4;
noPadBytes = npairs == 0?0:4 - npairs;
for(windex = 0; windex < noPadBytes; ++windex) {
bytes.readByte();
}
defaultOffset = bytes.readInt();
}
String name;
short index;
int classIndex;
int vindex;
int[] jumpTable;
int offset;
int var28;
int var30;
switch(opcode) {
case 18:
var28 = bytes.readUnsignedByte();
buf.append("").append(Class2HTML.toHTML(this.constantPool.constantToString(var28, this.constantPool.getConstant(var28).getTag()))).append("");
break;
case 19:
case 20:
index = bytes.readShort();
buf.append("").append(Class2HTML.toHTML(this.constantPool.constantToString(index, this.constantPool.getConstant(index).getTag()))).append("");
break;
case 21:
case 22:
case 23:
case 24:
case 25:
case 54:
case 55:
case 56:
case 57:
case 58:
case 169:
if(wide) {
vindex = bytes.readShort();
wide = false;
} else {
vindex = bytes.readUnsignedByte();
}
buf.append("%").append(vindex);
break;
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 59:
case 60:
case 61:
case 62:
case 63:
case 64:
case 65:
case 66:
case 67:
case 68:
case 69:
case 70:
case 71:
case 72:
case 73:
case 74:
case 75:
case 76:
case 77:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 84:
case 85:
case 86:
case 87:
case 88:
case 89:
case 90:
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
case 97:
case 98:
case 99:
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
case 108:
case 109:
case 110:
case 111:
case 112:
case 113:
case 114:
case 115:
case 116:
case 117:
case 118:
case 119:
case 120:
case 121:
case 122:
case 123:
case 124:
case 125:
case 126:
case 127:
case 128:
case 129:
case 130:
case 131:
case 133:
case 134:
case 135:
case 136:
case 137:
case 138:
case 139:
case 140:
case 141:
case 142:
case 143:
case 144:
case 145:
case 146:
case 147:
case 148:
case 149:
case 150:
case 151:
case 152:
case 172:
case 173:
case 174:
case 175:
case 176:
case 177:
case 190:
case 191:
case 194:
case 195:
default:
if(Const.getNoOfOperands(opcode) > 0) {
for(int i = 0; (long)i < Const.getOperandTypeCount(opcode); ++i) {
switch(Const.getOperandType(opcode, i)) {
case 8:
buf.append(bytes.readUnsignedByte());
break;
case 9:
buf.append(bytes.readShort());
break;
case 10:
buf.append(bytes.readInt());
break;
default:
throw new IllegalStateException("Unreachable default case reached! " + Const.getOperandType(opcode, i));
}
buf.append(" ");
}
}
break;
case 132:
short constant;
if(wide) {
vindex = bytes.readShort();
constant = bytes.readShort();
wide = false;
} else {
vindex = bytes.readUnsignedByte();
constant = bytes.readByte();
}
buf.append("%").append(vindex).append(" ").append(constant);
break;
case 153:
case 154:
case 155:
case 156:
case 157:
case 158:
case 159:
case 160:
case 161:
case 162:
case 163:
case 164:
case 165:
case 166:
case 167:
case 168:
case 198:
case 199:
var28 = bytes.getIndex() + bytes.readShort() - 1;
buf.append("").append(var28).append("");
break;
case 170:
int low = bytes.readInt();
int high = bytes.readInt();
offset = bytes.getIndex() - 12 - noPadBytes - 1;
defaultOffset += offset;
buf.append("");
jumpTable = new int[high - low + 1];
for(npairs = 0; npairs < jumpTable.length; ++npairs) {
jumpTable[npairs] = offset + bytes.readInt();
buf.append("").append(low + npairs).append(" | ");
}
buf.append("default | \n");
int[] var29 = jumpTable;
windex = jumpTable.length;
for(var30 = 0; var30 < windex; ++var30) {
int var31 = var29[var30];
buf.append("").append(var31).append(" | ");
}
buf.append("").append(defaultOffset).append(" | \n \n");
break;
case 171:
npairs = bytes.readInt();
offset = bytes.getIndex() - 8 - noPadBytes - 1;
jumpTable = new int[npairs];
defaultOffset += offset;
buf.append("\n");
break;
case 178:
case 179:
case 180:
case 181:
index = bytes.readShort();
ConstantFieldref c1 = (ConstantFieldref)this.constantPool.getConstant(index, (byte)9, ConstantFieldref.class);
classIndex = c1.getClassIndex();
name = this.constantPool.getConstantString(classIndex, (byte)7);
name = Utility.compactClassName(name, false);
var28 = c1.getNameAndTypeIndex();
String fieldName = this.constantPool.constantToString(var28, (byte)12);
if(name.equals(this.className)) {
buf.append("").append(fieldName).append("\n");
} else {
buf.append(this.constantHtml.referenceConstant(classIndex)).append(".").append(fieldName);
}
break;
case 182:
case 183:
case 184:
case 185:
case 186:
short mIndex = bytes.readShort();
if(opcode == 185) {
bytes.readUnsignedByte();
bytes.readUnsignedByte();
ConstantInterfaceMethodref c2 = (ConstantInterfaceMethodref)this.constantPool.getConstant(mIndex, (byte)11, ConstantInterfaceMethodref.class);
classIndex = c2.getClassIndex();
var28 = c2.getNameAndTypeIndex();
name = Class2HTML.referenceClass(classIndex);
} else if(opcode == 186) {
bytes.readUnsignedByte();
bytes.readUnsignedByte();
ConstantInvokeDynamic var32 = (ConstantInvokeDynamic)this.constantPool.getConstant(mIndex, (byte)18, ConstantInvokeDynamic.class);
var28 = var32.getNameAndTypeIndex();
name = "#" + var32.getBootstrapMethodAttrIndex();
} else {
ConstantMethodref var33 = (ConstantMethodref)this.constantPool.getConstant(mIndex, (byte)10, ConstantMethodref.class);
classIndex = var33.getClassIndex();
var28 = var33.getNameAndTypeIndex();
name = Class2HTML.referenceClass(classIndex);
}
String str = Class2HTML.toHTML(this.constantPool.constantToString(this.constantPool.getConstant(var28, (byte)12)));
ConstantNameAndType var34 = (ConstantNameAndType)this.constantPool.getConstant(var28, (byte)12, ConstantNameAndType.class);
String signature = this.constantPool.constantToString(var34.getSignatureIndex(), (byte)1);
String[] args = Utility.methodSignatureArgumentTypes(signature, false);
String type = Utility.methodSignatureReturnType(signature, false);
buf.append(name).append(".").append(str).append("").append("(");
for(int var35 = 0; var35 < args.length; ++var35) {
buf.append(Class2HTML.referenceType(args[var35]));
if(var35 < args.length - 1) {
buf.append(", ");
}
}
buf.append("):").append(Class2HTML.referenceType(type));
break;
case 187:
case 192:
case 193:
index = bytes.readShort();
buf.append(this.constantHtml.referenceConstant(index));
break;
case 188:
buf.append("").append(Const.getTypeName(bytes.readByte())).append("");
break;
case 189:
index = bytes.readShort();
buf.append(this.constantHtml.referenceConstant(index));
break;
case 196:
wide = true;
buf.append("(wide)");
break;
case 197:
index = bytes.readShort();
byte dimensions = bytes.readByte();
buf.append(this.constantHtml.referenceConstant(index)).append(":").append(dimensions).append("-dimensional");
break;
case 200:
case 201:
windex = bytes.getIndex() + bytes.readInt() - 1;
buf.append("").append(windex).append("");
}
buf.append(" | ");
return buf.toString();
}
private void findGotos(ByteSequence bytes, Code code) throws IOException {
this.gotoSet = new BitSet(bytes.available());
int defaultOffset;
int offset;
int var16;
if(code != null) {
CodeException[] remainder = code.getExceptionTable();
CodeException[] noPadBytes = remainder;
defaultOffset = remainder.length;
for(offset = 0; offset < defaultOffset; ++offset) {
CodeException npairs = noPadBytes[offset];
this.gotoSet.set(npairs.getStartPC());
this.gotoSet.set(npairs.getEndPC());
this.gotoSet.set(npairs.getHandlerPC());
}
Attribute[] var13 = code.getAttributes();
Attribute[] var15 = var13;
offset = var13.length;
for(var16 = 0; var16 < offset; ++var16) {
Attribute j = var15[var16];
if(j.getTag() == 5) {
((LocalVariableTable)j).forEach((var) -> {
int start = var.getStartPC();
this.gotoSet.set(start);
this.gotoSet.set(start + var.getLength());
});
break;
}
}
}
while(bytes.available() > 0) {
int opcode = bytes.readUnsignedByte();
int index;
switch(opcode) {
case 153:
case 154:
case 155:
case 156:
case 157:
case 158:
case 159:
case 160:
case 161:
case 162:
case 163:
case 164:
case 165:
case 166:
case 167:
case 168:
case 198:
case 199:
index = bytes.getIndex() + bytes.readShort() - 1;
this.gotoSet.set(index);
break;
case 169:
case 172:
case 173:
case 174:
case 175:
case 176:
case 177:
case 178:
case 179:
case 180:
case 181:
case 182:
case 183:
case 184:
case 185:
case 186:
case 187:
case 188:
case 189:
case 190:
case 191:
case 192:
case 193:
case 194:
case 195:
case 196:
case 197:
default:
bytes.unreadByte();
this.codeToHTML(bytes, 0);
break;
case 170:
case 171:
int var12 = bytes.getIndex() % 4;
int var14 = var12 == 0?0:4 - var12;
for(var16 = 0; var16 < var14; ++var16) {
bytes.readByte();
}
defaultOffset = bytes.readInt();
int var17;
if(opcode == 170) {
var16 = bytes.readInt();
var17 = bytes.readInt();
offset = bytes.getIndex() - 12 - var14 - 1;
defaultOffset += offset;
this.gotoSet.set(defaultOffset);
for(int j1 = 0; j1 < var17 - var16 + 1; ++j1) {
index = offset + bytes.readInt();
this.gotoSet.set(index);
}
} else {
var16 = bytes.readInt();
offset = bytes.getIndex() - 8 - var14 - 1;
defaultOffset += offset;
this.gotoSet.set(defaultOffset);
for(var17 = 0; var17 < var16; ++var17) {
bytes.readInt();
index = offset + bytes.readInt();
this.gotoSet.set(index);
}
}
break;
case 200:
case 201:
index = bytes.getIndex() + bytes.readInt() - 1;
this.gotoSet.set(index);
}
}
}
private void writeMethod(Method method, int methodNumber) throws IOException {
String signature = method.getSignature();
String[] args = Utility.methodSignatureArgumentTypes(signature, false);
String type = Utility.methodSignatureReturnType(signature, false);
String name = method.getName();
String htmlName = Class2HTML.toHTML(name);
String access = Utility.accessToString(method.getAccessFlags());
access = Utility.replace(access, " ", " ");
Attribute[] attributes = method.getAttributes();
this.printWriter.print("" + access + " " + Class2HTML.referenceType(type) + " " + htmlName + "(");
for(int c = 0; c < args.length; ++c) {
this.printWriter.print(Class2HTML.referenceType(args[c]));
if(c < args.length - 1) {
this.printWriter.print(", ");
}
}
this.printWriter.println(")
");
Code var27 = null;
byte[] code = null;
if(attributes.length > 0) {
this.printWriter.print("Attributes
\n");
for(int stream = 0; stream < attributes.length; ++stream) {
byte tag = attributes[stream].getTag();
if(tag != -1) {
this.printWriter.print("- " + Const.getAttributeName(tag) + "
\n");
} else {
this.printWriter.print("- " + attributes[stream] + "
");
}
if(tag == 2) {
var27 = (Code)attributes[stream];
Attribute[] offset = var27.getAttributes();
code = var27.getCode();
this.printWriter.print("");
}
}
this.printWriter.println("
");
}
if(code != null) {
ByteSequence var28 = new ByteSequence(code);
Throwable var29 = null;
try {
var28.mark(var28.available());
this.findGotos(var28, var27);
var28.reset();
this.printWriter.println("Byte offset | Instruction | Argument | ");
String anchor;
String anchor2;
String var31;
for(; var28.available() > 0; this.printWriter.println("
---|
" + anchor2 + " | " + anchor + var31 + " |
")) {
int var30 = var28.getIndex();
var31 = this.codeToHTML(var28, methodNumber);
anchor = "";
if(this.gotoSet.get(var30)) {
anchor = "";
}
if(var28.getIndex() == code.length) {
anchor2 = "" + var30 + "";
} else {
anchor2 = "" + var30;
}
}
} catch (Throwable var25) {
var29 = var25;
throw var25;
} finally {
if(var28 != null) {
if(var29 != null) {
try {
var28.close();
} catch (Throwable var24) {
var29.addSuppressed(var24);
}
} else {
var28.close();
}
}
}
this.printWriter.println(" |
");
this.printWriter.println("
");
}
}
}