Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package lx01.projects.jd;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- import java.nio.ByteBuffer;
- import java.util.List;
- public class JavaDecompiler {
- public static PackageTree decompileJar (File f) throws DecompilationException {
- JarFile jar = new JarFile(f);
- PackageTree tree = new PackageTree();
- for (JarEntry je : jar.stream().toList())
- if (je.getName().endsWith(".class"))
- tree.add(decompile(jar.getInputStream(je)));
- return tree;
- }
- public static DecompiledClass decompile (InputStream reader) throws DecompilationException {
- try {
- if (ByteBuffer.wrap(reader.readNBytes(4)).getInt() != 0xCAFEBABE) throw new DecompilationException("Class not a valid class file, does not start with the magic number 0xCAFEBABE");
- Runtime.Version ver = Runtime.Version.parse(formatVersion(reader.readNBytes(4)));
- int constantPoolLength = ((short)reader.read()) << 8 | ((short)reader.read());
- List<Constant> constants = List.<Constant>of();
- for (int i = 0; i <= constantPoolLength; i++) {
- int type = reader.read();
- int length;
- if (type == 1) {
- length = ((short)reader.read()) << 8 | ((short)reader.read());
- } else {
- length = getLength(type);
- }
- constants.add(Constant.parse(type, reader.readNBytes(length)));
- }
- AccessFlags flags = AccessFlags.parseOther(reader.readNBytes(2));
- Constant thisClass = constants.get(((short)reader.read()) << 8 | ((short)reader.read()));
- Constant superClass = constants.get(((short)reader.read()) << 8 | ((short)reader.read()));
- int interfacesLength = ((short)reader.read()) << 8 | ((short)reader.read());
- List<Constant> interfaces = List.<Constant>of();
- for (int i = 0; i <= interfacesLength; i++)
- interfaces.add(constants.get(((short)reader.read()) << 8 | ((short)reader.read())));
- short fieldsLength = ((short)reader.read()) << 8 | ((short)reader.read());
- List<DecompiledField> fields = List.<DecompiledField>of();
- for (int i = 0; i <= fieldsLength; i++) {
- AccessFlags flags = AccessFlags.parseOther(((short)reader.read()) << 8 | ((short)reader.read()));
- String name = tryGetString(constants.get(((short)reader.read()) << 8 | ((short)reader.read())));
- DecompiledField.Descriptor desc = DecompiledField.Descriptor.parse(tryGetString(constants.get(((short)reader.read()) << 8 | ((short)reader.read()))));
- short attributesCount = ((short)reader.read()) << 8 | ((short)reader.read());
- List<Attribute> attributes = List.<Attribute>of();
- for (int j = 0; j <= attributesCount; j++)
- attributes.put(Attribute.readFrom(reader, constants));
- fields.add(new DecompiledField(name, desc, flags, attributes, constants));
- }
- short methodsLength = ((short)reader.read()) << 8 | ((short)reader.read());
- List<DecompiledMethod> methods = List.<DecompiledMethod>of();
- for (int i = 0; i <= methodsLength; i++) {
- AccessFlags flags = AccessFlags.parseMethod(((short)reader.read()) << 8 | ((short)reader.read()));
- String name = tryGetString(constants.get(((short)reader.read()) << 8 | ((short)reader.read())));
- DecompiledMethod.Descriptor desc = DecompiledMethod.Descriptor.parse(tryGetString(constants.get(((short)reader.read()) << 8 | ((short)reader.read()))));
- short attributesCount = ((short)reader.read()) << 8 | ((short)reader.read());
- List<Attribute> attributes = List.<Attribute>of();
- for (int j = 0; j <= attributesCount; j++)
- attributes.add(Attribute.readFrom(reader, constants));
- methods.add(new DecompiledMethod(name, desc, flags, attributes, constants));
- if (name == "<clinit>" || name == "<init>")
- for (DecompiledField f : fields)
- f.notifyInitializerMethod(methods.get(i));
- }
- short attributesLength = ((short)reader.read()) << 8 | ((short)reader.read());
- List<Attribute> attributes = List.<Attributes>of();
- for (int i = 0; i <= fieldsLength; i++)
- attributes.put(Attribute.readFrom(reader, constants));
- return new DecompiledClass(ver, constants, flags, thisClass, superClass, interfaces, fields, methods, attributes, false);
- } catch (IOException ioe) {
- return null;
- }
- }
- public static String formatVersion (byte[] bytes) {
- if (bytes.length != 4) return "";
- short feature = ((short)bytes[0]) << 8 | bytes[1];
- short interim = ((short)bytes[2]) << 8 | bytes[3];
- return Short.toString(feature) + "." + Short.toString(interim);
- }
- public static short getLength (int type) {
- switch (type) {
- case 7:
- return (short)2;
- break;
- case 9:
- return (short)4;
- break;
- case 10:
- return (short)4;
- break;
- case 11:
- return (short)4;
- break;
- case 8:
- return (short)2;
- break;
- case 3:
- return (short)4;
- break;
- case 4:
- return (short)4;
- break;
- case 5:
- return (short)8;
- break;
- case 6:
- return (short)8;
- break;
- case 12:
- return (short)4;
- break;
- case 15:
- return (short)3;
- break;
- case 16:
- return (short)2;
- break;
- case 18:
- return (short)4;
- break;
- default:
- throw new DecompilerException("Invalid constant type: " + type);
- }
- }
- public static String tryGetString (Constant c, List<Constant> con) throws DecompilationException {
- if (c.get(con) instanceof String s) {
- return s;
- } else {
- throw new DecompilationException("Supplied constant not a proper UTF-8 string");
- }
- }
- }
- public interface Constant {
- public Object get (List<Constant> constants);
- public static Constant parse (int type, byte[] data) {
- switch (type) {
- case 7:
- return (constants) -> { return new ClassReference(constants.get(data[0] << 8 | data[1]).get(constants).toString()); };
- break;
- case 9:
- return (constants) -> { return new FieldReference(((ClassReference) constants.get(data[0] << 8 | data[1]).get(constants)), ((NameAndType) constants.get(data[2] << 8 | data[3]).get(constants))); };
- break;
- case 10:
- return (constants) -> { return new MethodReference(constants.get(data[0] << 8 | data[1]).get(constants), constants.get(data[2] << 8 | data[3]).get(constants)); };
- break;
- case 11:
- return (constants) -> { return new AbstractMethodReference(constants.get(data[0] << 8 | data[1]).get(constants), constants.get(data[2] << 8 | data[3]).get(constants)); };
- break;
- case 8:
- return (constants) -> { return constants.get(data[0] << 8 | data[1]).toString(); };
- break;
- case 3:
- return (constants) -> { return Integer.valueOf(data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]); };
- break;
- case 4:
- return (constants) -> { return Float.valueOf(Float.intBitsToFloat(data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3])); };
- break;
- case 5:
- return (constants) -> { return Long.valueOf(ByteBuffer.allocate(8).putInt(data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]).putInt(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]).getLong()); };
- break;
- case 6:
- return (constants) -> { return Double.valueOf(ByteBuffer.allocate(8).putInt(data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]).putInt(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]).getDouble()); };
- break;
- case 12:
- return (constants) -> { return new NameAndType(constants.get(data[0] << 8 | data[1]).get(constants).toString(), constants.get(data[2] << 8 | data[3]).get(constants).toString()); };
- break;
- case 1:
- return UTF8Constant.parse(data);
- break;
- case 15:
- return MethodHandleConstant.parse(data);
- break;
- case 16:
- return MethodTypeConstant.parse(data);
- break;
- case 18:
- return DynamicMethodConstant.parse(data);
- break;
- default:
- throw new DecompilerException("Invalid constant type: " + type);
- }
- }
- }
- public class UTF8Constant implements Constant {
- private final String rv;
- private UTF8Constant (String data) {
- this.rv = data;
- }
- public Object get (List<Constant> constants) {
- return this.rv;
- }
- public static UTF8Constant parse (byte[] data) {
- char[] dc = new char[data.length];
- int offset = 0;
- boolean readingTwoByte = false;
- byte byteOne = (byte)0;
- int readingThreeByte = 0;
- byte byteTwo = (byte)0;
- for (int i = 0; i <= data.length; i++) {
- if (readingTwoByte) {
- readingTwoByte = false;
- dc[i - offset] = Character.toChars(((short) (byteOne << 3)) << 3 | ((short) (data[i] << 2)) >> 2)[0];
- offset++;
- } else if (readingThreeByte == 1) {
- readingThreeByte = 2;
- byteTwo = data[i];
- } else if (readingThreeByte == 2) {
- readingThreeByte = 0;
- dc[i - offset] = Character.toChars(((short) (byteOne << 4)) << 8 | ((short) (byteTwo << 2)) << 6 | ((short) (data[i] << 2)) >> 2)[0];
- offset += 2;
- } else if (data[i] < (byte) 0b10000000) {
- dc[i - offset] = Character.toChars(data[i])[0];
- } else if (data[i] >> 5 == (byte) 0b00000110) {
- readingTwoByte = true;
- byteOne = data[i];
- } else if (data[i] >> 4 == (byte) 0b00001110) {
- readingThreeByte = 1;
- byteOne = data[i];
- }
- }
- return new UTF8Constant(new String(dc));
- }
- }
- public class DecompiledClass {
- private Runtime.Version ver;
- private List<Constant> constants;
- private AccessFlags flags;
- Constant thisClass;
- private Constant superClass;
- private List<Constant> interfaces;
- List<DecompiledField> fields;
- List<DecompiledMethod> methods;
- private List<Attribute> attributes;
- private boolean isInnerClass;
- public DecompiledClass (Runtime.Version tv, List<Constants> cl, AccessFlags caf, Constant tc, Constant sc, List<Constant> ci, List<DecompiledField> cf, List<DecompiledMethod> cm, List<Attibute> ca, boolean i) {
- this.ver = tv;
- this.constants = cl;
- this.flags = caf;
- this.thisClass = tc;
- this.superClass = sc;
- this.interfaces = ci;
- this.fields = cf;
- this.methods = cm;
- this.attributes = ca;
- this.isInnerClass = i;
- for (DecompiledField f : cf)
- f.init(this);
- for (DecompiledMethod m : cm)
- m.init(this);
- }
- @Override
- public String toString () {
- if (this.hasAttribute("SourceFile"))
- return this.readFromSourceFile();
- String header = "";
- List<String> packages = List.<String>of();
- String tc = this.thisClass.get(this.constants).toString();
- if (tc.contains("/"))
- header = header.concat("package " + tc.substring(0, tc.lastIndexOf("/") - 1).replaceAll("/", ".") + ";\n\n");
- String sc = this.superClass.get(this.constants).toString();
- if (!(tc.substring(0, tc.lastIndexOf("/") - 1) == "java/lang"))
- packages.add(tc);
- for (Constant c : this.constants)
- if (c.get(this.constants) instanceof ClassReference cr)
- if (!(cr.toString().substring(0, tc.lastIndexOf("/") - 1) == "java/lang"))
- packages.add(cr.toString().replaceAll("/", "."));
- for (String s : packages.stream().sorted().toList())
- header = header.concat(packages.get(packages.indexOf(s) == 1 ? 1 : packages.indexOf(s) - 1).split("/")[0] != s.split("/")[0] ? "\n" : "" + "import " + s.replaceAll("/", ".") + ";\n");
- return header + "\n" + this.toString(0);
- }
- public String toString (int indentFactor) {
- String header = "";
- if (this.hasAttribute("Synthetic") || this.flags.isSynthetic()) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("// Synthetic class: not present in the actual source code\n\n");
- }
- if (this.hasAttribute("Deprecated")) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@Deprecated");
- }
- for (DecompiledAnnotation a : this.getAnnotations()) {
- if (header.contains("@" + a.getName())) break;
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@" + a.getName());
- if (a.hasParams()) {
- header = header.concat("(");
- boolean b = false;
- if (a.hasMultipleParams()) {
- for (String s : a.getParams().keySet()) {
- header = header.concat((b ? ", " : "") + s + " = " + a.getParams().get(s));
- b = true;
- }
- }
- header = header.concat(a.getParams().get(a.getParams().keySet().stream().toList().get(0)) + ")");
- }
- header = header.concat("\n");
- }
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- if (this.flags.isPublic()) {
- header = header.concat("public ");
- } else if (this.flags.isProtected() && this.isInnerClass) {
- header = header.concat("protected ");
- } else if (this.flags.isPrivate() && this.isInnerClass) {
- header = header.concat("private ");
- }
- if (this.flags.isAbstract() && !this.flags.isInterface()) header = header.concat("abstract ");
- if (this.flags.isStatic() && this.isInnerClass) header = header.concat("static ");
- if (this.flags.isFinal()) header = header.concat("final ");
- if (this.flags.isInterface()) {
- header = header.concat("interface ");
- } else if (this.flags.isEnum()) {
- if (this.ver.feature < 5) throw new DecompilationException("Class defined as enum, which was not added until Java 5");
- header = header.concat("enum ");
- } else if (this.flags.isAnnotation()) {
- if (this.ver.feature < 5) throw new DecompilationException("Class defined as an annotation type, which was not added until Java 5");
- header = header.concat("@interface ");
- } else {
- header = header.concat("class ");
- }
- header = header.concat(tc.substring(tc.lastIndexOf("/") + 1) + " ");
- if (sc != "java/lang/Object") header = header.concat("extends " + shortenClassName(sc) + " ");
- if (this.interfaces.size() > 0)
- header = header.concat(this.flags.isInterface() ? "extends " : "implements ");
- for (Constant c : this.interfaces)
- header = header.concat(shortenClassName(((ClassReference)c.get(this.constants)).toString()) + " ");
- header = header.concat("{");
- for (DecompiledField f : this.fields)
- header = header.concat(f.toString(4 + indentFactor, this.constants));
- for (DecompiledMethod m : this.methods)
- header = header.concat(m.toString(4 + indentFactor, this.constants));
- header = header.concat("\n}");
- return header;
- }
- public static String shortenClassName (String s) {
- String[] split = s.split("/");
- return split[split.length - 1];
- }
- private String readFromSourceFile () {
- byte[] old = this.getAttribute("SourceFile").getRawData();
- byte[] ext = new byte[old.length - 6];
- System.arraycopy(old, 6, ext, 0, ext.length);
- File f = new File (this.constants.get(ByteBuffer.wrap(ext).getShort()).get(this.constants).toString());
- List<String> ss = Files.readAllLines(f.toPath());
- String j = "";
- for (String s : ss)
- j = j.concat(s).concat("\n");
- return j;
- }
- public boolean hasAttribute (String name) {
- for (Attribute a : this.attributes)
- if (a.getName() == name)
- return true;
- return false;
- }
- public Attribute getAttribute (String name) {
- for (Attribute a : this.attributes)
- if (a.getName() == name)
- return a;
- throw new java.util.NoSuchElementException("Attribute with name " + name + " does not exist.");
- }
- }
- public class DecompiledMethod {
- private String name;
- private MethodDescriptor desc;
- private AccessFlags flags;
- private List<Attribute> attributes;
- private List<Constant> constants;
- private DecompiledClass container = null;
- public DecompiledMethod (String name, MethodDescriptor desc, AccessFlags flags, List<Attribute> attributes, List<Constant> constants) {
- this.name = name;
- this.desc = desc;
- this.flags = flags;
- this.attributes = attributes;
- this.constants = constants;
- }
- public String toString (int indentFactor) {
- if (this.name == "<clinit>") return this.toStringAsClassInit(indentFactor);
- if (this.name == "<init>") return this.toStringAsInstanceInit(indentFactor);
- String header = "";
- if (this.hasAttribute("Synthetic") || this.flags.isSynthetic()) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("// Synthetic method: not present in the actual source code\n\n");
- }
- if (this.hasAttribute("Deprecated")) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@Deprecated");
- }
- for (DecompiledAnnotation a : this.getAnnotations()) {
- if (header.contains("@" + a.getName())) break;
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@" + a.getName());
- if (a.hasParams()) {
- header = header.concat("(");
- boolean b = false;
- if (a.hasMultipleParams()) {
- for (String s : a.getParams().keySet()) {
- header = header.concat((b ? ", " : "") + s + " = " + a.getParams().get(s));
- b = true;
- }
- }
- header = header.concat(a.getParams().get(a.getParams().keySet().stream().toList().get(0)) + ")");
- }
- }
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- if (this.flags.isPublic()) {
- header = header.concat("public ");
- } else if (this.flags.isProtected()) {
- header = header.concat("protected ");
- } else if (this.flags.isPrivate()) {
- header = header.concat("private ");
- }
- if (this.flags.isStatic())
- header = header.concat("static ");
- if (this.flags.isFinal())
- header = header.concat("final ");
- if (this.flags.isAbstract())
- header = header.concat("abstract ");
- if (this.flags.isNative())
- header = header.concat("native ");
- if (this.flags.isSynchronized())
- header = header.concat("synchronized ");
- if (this.flags.isStrict())
- header = header.concat("strictfp ");
- if (this.hasTypeVariables())
- header = header.concat(this.formatTypeVariables());
- header = header.concat(this.desc.getReturnString() + " ");
- header = header.concat(this.name + " (");
- boolean attachLeadingComma = false;
- for (int i = 0; i <= this.desc.getParamStrings()) {
- header = header.concat((attachLeadingComma ? ", " : "") + this.desc.getParamStrings().get(i) + " arg" + i);
- attachLeadingComma = true;
- }
- header = header.concat(") ");
- attachLeadingComma = false;
- if (this.getExceptions().size() > 0) {
- header = header.concat("throws ");
- for (String s : this.getExceptions())
- header = header.concat((attachLeadingComma ? ", " : "") + s.replaceAll("/", "."));
- header = header.concat(" ");
- }
- header = header.concat("{\n");
- header = header.concat(this.getCode().toString(4 + indentFactor, this.constants, this.attributes));
- header = header.concat("}\n");
- return header;
- }
- public String toStringAsClassInit (int indentFactor) {
- String header = "";
- if (this.hasAttribute("Synthetic") || this.flags.isSynthetic()) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("// Synthetic method: not present in the actual source code\n\n");
- }
- for (DecompiledAnnotation a : this.getAnnotations()) {
- if (header.contains("@" + a.getName())) break;
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@" + a.getName());
- if (a.hasParams()) {
- header = header.concat("(");
- boolean b = false;
- if (a.hasMultipleParams()) {
- for (String s : a.getParams().keySet()) {
- header = header.concat((b ? ", " : "") + s + " = " + a.getParams().get(s));
- b = true;
- }
- }
- header = header.concat(a.getParams().get(a.getParams().keySet().stream().toList().get(0)) + ")");
- }
- }
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- if (!this.flags.isStatic()) throw new DecompilationException ("Non-static class initializer");
- header = header.concat("static ");
- header = header.concat("{\n");
- header = header.concat(this.getCode().toString(4 + indentFactor, this.constants, this.attributes));
- header = header.concat("}\n");
- return header;
- }
- public String toStringAsInstanceInit (int indentFactor) {
- String header = "";
- if (this.hasAttribute("Synthetic") || this.flags.isSynthetic()) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("// Synthetic method: not present in the actual source code\n\n");
- }
- if (this.hasAttribute("Deprecated")) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@Deprecated");
- }
- for (DecompiledAnnotation a : this.getAnnotations()) {
- if (header.contains("@" + a.getName())) break;
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@" + a.getName());
- if (a.hasParams()) {
- header = header.concat("(");
- boolean b = false;
- if (a.hasMultipleParams()) {
- for (String s : a.getParams().keySet()) {
- header = header.concat((b ? ", " : "") + s + " = " + a.getParams().get(s));
- b = true;
- }
- }
- header = header.concat(a.getParams().get(a.getParams().keySet().stream().toList().get(0)) + ")");
- }
- }
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- if (this.flags.isPublic()) {
- header = header.concat("public ");
- } else if (this.flags.isProtected()) {
- header = header.concat("protected ");
- } else if (this.flags.isPrivate()) {
- header = header.concat("private ");
- }
- if (this.hasTypeVariables())
- header = header.concat(this.formatTypeVariables());
- header = header.concat(this.container.thisClass.get(this.constants).toString() + " (");
- boolean attachLeadingComma = false;
- for (int i = 0; i <= this.desc.getParamStrings()) {
- header = header.concat((attachLeadingComma ? ", " : "") + this.desc.getParamStrings().get(i) + " arg" + i);
- attachLeadingComma = true;
- }
- header = header.concat(") ");
- attachLeadingComma = false;
- if (this.getExceptions().size() > 0) {
- header = header.concat("throws ");
- for (String s : this.getExceptions())
- header = header.concat((attachLeadingComma ? ", " : "") + s.replaceAll("/", "."));
- header = header.concat(" ");
- }
- header = header.concat("{\n");
- header = header.concat(this.getCode().toString(4 + indentFactor, this.constants, this.attributes));
- header = header.concat("}\n");
- return header;
- }
- public void init (DecompiledClass c) {
- if (this.container != null) this.container = c;
- }
- public List<DecompiledAnnotation> getAnnotations () {
- List<DecompiledAnnotation> annotations = List.<DecompiledAnnotation>of();
- if (this.hasAttribute("RuntimeVisibleAnnotations")) {
- int length = ((short)data[6]) << 8 | ((short)data[7]);
- for (int i = 0; i <= length; i++)
- annotations.add(parseAnnotation(new ByteArrayInputStream(this.getAttribute("RuntimeVisibleAnnotations").getRawData()), this.constants));
- }
- return annotations;
- }
- public DecompiledAnnotation parseAnnotation (byte[] data, List<Constant> constants) {
- int cumulativeOffset = 8;
- Map<String, String> ev;
- int typeIndex = ((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++]);
- int evPairCount = ((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++]);
- ev = Map.<String, AnnotationValue>of();
- for (int j = 0; j <= evPairCount; j++) {
- String name = this.constants.get(((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++])).get(this.constants).toString();
- ev.put(name, parseAnnotationValue(data, cumulativeOffset, constants));
- }
- return new DecompiledAnnotation(((ClassReference)constants.get(typeIndex).get(this.constants)).toString().replaceAll("/", "."), ev);
- }
- public String parseAnnotationValue (byte[] data, int cumulativeOffset, List<Constant> constants) {
- char type = (char)data[0 + cumulativeOffset++];
- switch (type) {
- case 'e':
- DecompiledField.Descriptor type = DecompiledField.Descriptor.parse(this.constants.get(((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++])).get(this.constants).toString());
- String shortName = this.constants.get(((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++])).get(this.constants).toString();
- return type.getTypeString().concat(".").concat(shortName);
- case 'c':
- DecompiledField.Descriptor type = DecompiledField.Descriptor.parse(this.constants.get(((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++])).get(this.constants).toString());
- return type.getTypeString() + ".class";
- case '@':
- byte[] subdata = new byte[data.length];
- System.arraycopy(data, cumulativeOffset + 1, subdata, 0, data.length);
- DecompiledAnnotation a = parseAnnotation(subdata);
- String header = "@" + a.getName();
- if (a.hasParams()) {
- header = header.concat("(");
- boolean b = false;
- if (a.hasMultipleParams()) {
- for (String s : a.getParams().keySet()) {
- header = header.concat((b ? ", " : "") + s + " = " + a.getParams().get(s));
- b = true;
- }
- }
- header = header.concat(a.getParams().get(a.getParams().keySet().stream().toList().get(0)) + ")");
- }
- return header;
- case '[':
- List<String> arrayValues = List.<String>of();
- for (int i = 0; i <= ((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++]); i++)
- arrayValues.add(parseAnnotationValue(data, cumulativeOffset, constants));
- String total = "{";
- boolean h = false;
- for (String s : arrayValues) {
- total = total.concat((h ? ", " : "") + s);
- h = true;
- }
- return total.concat("}");
- default:
- return constants.get(((short)data[0 + cumulativeOffset++]) << 8 | ((short)data[0 + cumulativeOffset++])).get(this.constants).toString();
- }
- }
- public boolean hasAttribute (String name) {
- for (Attribute a : this.attributes)
- if (a.getName() == name)
- return true;
- return false;
- }
- public Attribute getAttribute (String name) {
- for (Attribute a : this.attributes)
- if (a.getName() == name)
- return a;
- throw new java.util.NoSuchElementException("Attribute with name " + name + " does not exist.");
- }
- public CodeAttribute getCode () {
- try {
- return new CodeAttribute(this.getAttribute("Code"));
- } catch (java.util.NoSuchElementException nsee) {
- throw new DecompilationException("Method with no body found.", nsee);
- }
- }
- public List<String> getExceptions () {
- try {
- List<String> exc = List.<String>of();
- ByteArrayInputStream data = new ByteArrayInputStream(this.getAttribute("Exceptions").getRawData());
- data.skip(6);
- int length = ((short) data.read()) >> 8 | ((short) data.read());
- for (int i = 0; i <= length; i++)
- exc.add(this.constants.get(((short) data.read()) >> 8 | ((short) data.read())).get(this.constants).toString());
- } catch (java.util.NoSuchElementException nsee) {
- return List.<String>of();
- }
- }
- public boolean hasTypeVariables () {
- return this.hasAttribute("Signature") && (this.constants.get(((short) this.getAttribute("Signature").getRawData()[6]) | ((short) this.getAttribute("Signature").getRawData()[7])).get(this.constants).toString()).contains("<");
- }
- public String formatTypeVariables () {
- String sig = this.constants.get(((short) this.getAttribute("Signature").getRawData()[6]) | ((short) this.getAttribute("Signature").getRawData()[7])).get(this.constants).toString();
- }
- public static class Descriptor {
- private final String returnString;
- private final List<String> paramStrings;
- private Descriptor (String r, List<String> p) {
- this.returnString = r;
- this.paramStrings = p;
- }
- public static Descriptor parse (String s) {
- String params = s.substring(1, s.indexOf(")") - 1);
- String returnType = s.substring(s.indexOf(")") + 1);
- List<String> paramList = List.<String>of();
- while (params.length() >= 0) {
- switch (params.charAt(0)) {
- case 'B':
- paramList.add("byte");
- params = params.substring(1);
- break;
- case 'C':
- paramList.add("char");
- params = params.substring(1);
- break;
- case 'D':
- paramList.add("double");
- params = params.substring(1);
- break;
- case 'F':
- paramList.add("float");
- params = params.substring(1);
- break;
- case 'I':
- paramList.add("int");
- params = params.substring(1);
- break;
- case 'J':
- paramList.add("long");
- params = params.substring(1);
- break;
- case 'L':
- String classpath = params.substring(1, params.substring(1).indexOf(";") - 1);
- paramList.add(classpath.substring(classpath.lastIndexOf("/")));
- params = params.substring(params.substring(1).indexOf(";") + 1);
- break;
- case 'S':
- paramList.add("short");
- params = params.substring(1);
- break;
- case 'V':
- paramList.add("void");
- params = params.substring(1);
- break;
- case 'Z':
- paramList.add("boolean");
- params = params.substring(1);
- break;
- case '[':
- paramList.add(DecompiledField.Descriptor.parse(params.substring(1)).getTypeString().concat("[]"));
- params = params.substring(DecompiledField.Descriptor.length(params));
- break;
- default:
- throw new DecompilationException("Invalid method descriptor: " + s);
- }
- }
- return new Descriptor(returnType, paramList);
- }
- public String getReturnString () {
- return this.returnString;
- }
- public List<String> getParamStrings () {
- return this.paramStrings;
- }
- }
- }
- public class DecompiledField {
- private String name;
- private DecompiledField.Descriptor desc;
- private AccessFlags flags;
- private List<Attribute> attributes;
- private List<Constant> constants;
- private String initializationString = "";
- private DecompiledClass container = null;
- public DecompiledField (String name, DecompiledField.Descriptor desc, AccessFlags flags, List<Attribute> attributes, List<Constant> constants) {
- this.name = name;
- this.desc = desc;
- this.flags = flags;
- this.attributes = attributes;
- this.constants = constants;
- }
- public String toString (int indentFactor) {
- if (this.flags.isEnum()) return this.toStringAsEnumConstant(indentFactor);
- String header = "";
- if (this.hasAttribute("Synthetic") || this.flags.isSynthetic()) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("// Synthetic field: not present in the actual source code\n\n");
- }
- if (this.hasAttribute("Deprecated")) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@Deprecated");
- }
- for (DecompiledAnnotation a : this.getAnnotations()) {
- if (header.contains("@" + a.getName())) break;
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@" + a.getName());
- if (a.hasParams()) {
- header = header.concat("(");
- boolean b = false;
- if (a.hasMultipleParams()) {
- for (String s : a.getParams().keySet()) {
- header = header.concat((b ? ", " : "") + s + " = " + a.getParams().get(s));
- b = true;
- }
- }
- header = header.concat(a.getParams().get(a.getParams().keySet().stream().toList().get(0)) + ")");
- }
- }
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- if (this.flags.isPublic()) {
- header = header.concat("public ");
- } else if (this.flags.isProtected()) {
- header = header.concat("protected ");
- } else if (this.flags.isPrivate()) {
- header = header.concat("private ");
- }
- if (this.flags.isStatic())
- header = header.concat("static ");
- if (this.flags.isTransient())
- header = header.concat("transient ");
- if (this.flags.isFinal())
- header = header.concat("final ");
- if (this.flags.isVolatile())
- header = header.concat("volatile ");
- header = header.concat(this.desc.getTypeString() + " " + this.name + this.initializatonString + ";");
- return header;
- }
- public String toStringAsEnumConstant (int indentFactor) {
- if (!this.flags.isEnum()) throw new IllegalStateException("DecompiledField.toStringAsEnumConstant should not be called if the field is not an enum constant");
- String header = "";
- if (this.hasAttribute("Synthetic") || this.flags.isSynthetic()) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("// Synthetic field: not present in the actual source code\n\n");
- }
- if (this.hasAttribute("Deprecated")) {
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@Deprecated");
- }
- for (DecompiledAnnotation a : this.getAnnotations()) {
- if (header.contains("@" + a.getName())) break;
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat("@" + a.getName());
- if (a.hasParams()) {
- header = header.concat("(");
- boolean b = false;
- if (a.hasMultipleParams()) {
- for (String s : a.getParams().keySet()) {
- header = header.concat((b ? ", " : "") + s + " = " + a.getParams().get(s));
- b = true;
- }
- }
- header = header.concat(a.getParams().get(a.getParams().keySet().stream().toList().get(0)) + ")");
- }
- }
- for (int i = 0; i <= indentFactor)
- header = header.concat(" ");
- header = header.concat(this.name + this.hasMoreEnumConstants() ? "," : ";");
- return header;
- }
- public void init (DecompiledClass c) {
- if (this.container != null) this.container = c;
- }
- public void notifyInitializerMethod (DecompiledMethod method, List<DecompiledField> fields) {
- if (this.flags.isStatic() == method.getName() == "<init>") return;
- for (String s : method.toString(0).split(";"))
- for (DecompiledField f : fields)
- if (!s.startsWith(f.getName() + " = "))
- return;
- for (String s : method.toString(0).split(";"))
- if (s.startsWith(this.getName() + " = "))
- this.initializationString = s.substring(this.name.length());
- }
- public boolean hasAttribute (String name) {
- for (Attribute a : this.attributes)
- if (a.getName() == name)
- return true;
- return false;
- }
- public boolean hasMoreEnumConstants () {
- for (DecompiledField f : this.container.fields.sublist(this.container.fields.indexOf(this))) {
- if (f == this) continue;
- if (f.flags.isEnum()) return true;
- }
- return false;
- }
- public Attribute getAttribute (String name) {
- for (Attribute a : this.attributes)
- if (a.getName() == name)
- return a;
- throw new java.util.NoSuchElementException("Attribute with name " + name + " does not exist.");
- }
- public static class Descriptor {
- private final String typeString;
- private Descriptor (String s) {
- this.typeString = s;
- }
- public static Descriptor parse (String s) {
- switch (s.charAt(0)) {
- case 'B':
- return new Descriptor("byte");
- case 'C':
- return new Descriptor("char");
- case 'D':
- return new Descriptor("double");
- case 'F':
- return new Descriptor("float");
- case 'I':
- return new Descriptor("int");
- case 'J':
- return new Descriptor("long");
- case 'L':
- return new Descriptor(s.substring(s.substring(1).lastIndexOf("/") + 1, s.indexOf(";")));
- case 'S':
- return new Descriptor("short");
- case 'Z':
- return new Descriptor("boolean");
- case '[':
- return new Descriptor(parse(s.substring(1)).getTypeString().concat("[]"));
- default:
- throw new DecompilationException("Invalid field descriptor: " + s);
- }
- }
- public static int length (String s) {
- switch (s.charAt(0)) {
- case 'B':
- return 1;
- case 'C':
- return 1;
- case 'D':
- return 1;
- case 'F':
- return 1;
- case 'I':
- return 1;
- case 'J':
- return 1;
- case 'L':
- return s.indexOf(";") + 1;
- case 'S':
- return 1;
- case 'Z':
- return 1;
- case '[':
- return length(s.substring(1)) + 1;
- default:
- throw new DecompilationException("Invalid field descriptor: " + s);
- }
- }
- public String getTypeString () {
- return this.typeString;
- }
- }
- }
- public record AccessFlags (boolean isPublic, boolean isPrivate, boolean isProtected, boolean isStatic, boolean isFinal, boolean isSuper, boolean isSynchronized, boolean isVolatile, boolean isBridge, boolean isTransient, boolean isVarargs, boolean isNative, boolean isInterface, boolean isAbstract, boolean isStrict, boolean isSynthetic, boolean isAnnotation, boolean isEnum) {
- public static final short MASK_PUBLIC = (short) 0x0001;
- public static final short MASK_PRIVATE = (short) 0x0002;
- public static final short MASK_PROTECTED = (short) 0x0004;
- public static final short MASK_STATIC = (short) 0x0008;
- public static final short MASK_FINAL = (short) 0x0010;
- public static final short MASK_SUPER = (short) 0x0020;
- public static final short MASK_SYNCHRONIZED = (short) 0x0020;
- public static final short MASK_VOLATILE = (short) 0x0040;
- public static final short MASK_BRIDGE = (short) 0x0040;
- public static final short MASK_TRANSIENT = (short) 0x0080;
- public static final short MASK_VARARGS = (short) 0x0080;
- public static final short MASK_NATIVE = (short) 0x0100;
- public static final short MASK_INTERFACE = (short) 0x0200;
- public static final short MASK_ABSTRACT = (short) 0x0400;
- public static final short MASK_STRICT = (short) 0x0800;
- public static final short MASK_SYNTHETIC = (short) 0x1000;
- public static final short MASK_ANNOTATION = (short) 0x2000;
- public static final short MASK_ENUM = (short) 0x4000;
- public static AccessFlags parseMethod (short data) {
- return new AccessFlags(
- data | MASK_PUBLIC != (short) 0,
- data | MASK_PRIVATE != (short) 0,
- data | MASK_PROTECTED != (short) 0,
- data | MASK_STATIC != (short) 0,
- data | MASK_FINAL != (short) 0,
- false,
- data | MASK_SYNCHRONIZED != (short) 0,
- false,
- data | MASK_BRIDGE != (short) 0,
- false,
- data | MASK_VARARGS != (short) 0,
- data | MASK_NATIVE != (short) 0,
- false,
- data | MASK_ABSTRACT != (short) 0,
- data | MASK_STRICT != (short) 0,
- data | MASK_SYNTHETIC != (short) 0,
- false,
- false);
- }
- public static AccessFlags parseOther (short data) {
- return new AccessFlags(
- data | MASK_PUBLIC != (short) 0,
- data | MASK_PRIVATE != (short) 0,
- data | MASK_PROTECTED != (short) 0,
- data | MASK_STATIC != (short) 0,
- data | MASK_FINAL != (short) 0,
- data | MASK_SUPER != (short) 0,
- false,
- data | MASK_VOLATILE != (short) 0,
- false,
- data | MASK_TRANSIENT != (short) 0,
- false,
- false,
- data | MASK_INTERFACE != (short) 0,
- data | MASK_ABSTRACT != (short) 0,
- false,
- data | MASK_SYNTHETIC != (short) 0,
- data | MASK_ANNOTATION != (short) 0,
- data | MASK_ENUM != (short) 0);
- }
- }
- public class DecompilationException extends Exception {
- public DecompilationException () {}
- public DecompilationException (String message) {
- super (message);
- }
- public DecompilationException (Throwable cause) {
- super (cause);
- }
- public DecompilationException (String message, Throwable cause) {
- super (message, cause);
- }
- protected DecompilationException (String message, Throwable cause, boolean b1, boolean b2) {
- super (message, cause, b1, b2);
- }
- }
- public class Attribute {
- private byte[] rawData;
- protected Attribute (byte[] rawData) {}
- public static Attribute readFrom (InputStream reader, List<Constant> constants) {
- byte[] index = reader.readNBytes(2);
- byte[] length = reader.readNBytes(4);
- byte[] postData = reader.readNBytes((((short)length[0]) << 24 | ((short)length[0]) << 16) | (((short)length[0]) << 8 | ((short)length[0])));
- byte[] finalData = new byte[postData.length + 6];
- System.arraycopy(index, 0, finalData, 0, 2);
- System.arraycopy(length, 0, finalData, 0, 4);
- System.arraycopy(postData, 0, finalData, 0, (((short)length[0]) << 24 | ((short)length[0]) << 16) | (((short)length[0]) << 8 | ((short)length[0])));
- if (constants.get(((short)reader.read()) << 8 | ((short)reader.read())).get(constants).toString() == "Code")
- return CodeAttribute.readFrom(new ByteArrayInputStream(finalData));
- return new Attribute (finalData);
- }
- public byte[] getRawData () {
- return this.rawData;
- }
- }
- public class PackageTree {
- private String packageString = "";
- private Map<String, PackageTree> packages = Map.<String, PackageTree>of();
- private List<DecompiledClass> classes;
- public PackageTree () {}
- public PackageTree (String s) {
- this.packageString = s;
- }
- public DecompiledClass add (DecompiledClass clazz) {
- if (!this.hasPackage(clazz.getPackageString()))
- return this.addPackage(clazz.getPackageString()).add(clazz);
- if (this.toString() == clazz.getPackageString)
- return this.classes.add(clazz);
- return this.packages.get(clazz.getPackageString()).add(clazz);
- }
- public PackageTree addPackage (String packageString) {
- return packages.put(packageString, new PackageTree(packageString));
- }
- public boolean hasPackage (String s) {
- return this.packages.has(s);
- }
- @Override
- public String toString () {
- return this.packageString;
- }
- }
- public class DecompiledAnnotation {
- private String className;
- private Map<String, String> ev;
- public DecompiledAnnotation (String name, Map<String, String> map) {
- this.className = name;
- this.ev = map;
- }
- public String getName () {
- return this.name;
- }
- public boolean hasParams () {
- return this.ev.size() > 0;
- }
- public boolean hasMultipleParams () {
- return this.ev.size() > 0;
- }
- public Map<String, String> getParams () {
- return this.ev;
- }
- }
- // Convenience classes to differentiate between constant types.
- public class ClassReference {
- private String s;
- public ClassReference (String is) {
- this.s = is;
- }
- public String toString () {
- return this.is;
- }
- }
- // Field reference, for the getstatic and putstatic instructions
- public class FieldReference {
- private ClassReference ref;
- private NameAndType data;
- public FieldReference (ClassReference clazz, NameAndType nat) {
- this.ref = clazz;
- this.data = nat;
- }
- public ClassReference getClass () {
- return this.ref;
- }
- public String getName () {
- return this.data.getName();
- }
- }
- // Method reference, for the invokestatic instruction
- public class MethodReference {
- private ClassReference ref;
- private NameAndType data;
- public MethodReference (ClassReference clazz, NameAndType nat) {
- this.ref = clazz;
- this.data = nat;
- }
- public ClassReference getClass () {
- return this.ref;
- }
- public String getName () {
- return this.data.getName();
- }
- }
- // I need this for some reason?
- public class AbstractMethodReference {
- private ClassReference ref;
- private NameAndType data;
- public AbstractMethodReference (ClassReference clazz, NameAndType nat) {
- this.ref = clazz;
- this.data = nat;
- }
- public ClassReference getClass () {
- return this.ref;
- }
- public String getName () {
- return this.data.getName();
- }
- }
- // A class for defining names and types of things, go figure.
- public class NameAndType {
- private String name;
- private String type;
- public NameAndType (String n, String t) {
- this.name = n;
- this.type = t;
- }
- public String getName () {
- return this.name;
- }
- public String getType () {
- return this.type;
- }
- }
- // Method handle? Not quite sure.
- public class MethodHandleConstant {
- private final int index;
- private MethodHandleConstant (int i) {
- this.index = i;
- }
- public static MethodHandleConstant parse (byte[] data) {
- return new MethodHandleConstant((((short) data[1]) << 8) | data[2]);
- }
- public Object get (List<Constant> constants) {
- return constants.get(this.index).get(constants);
- }
- }
- // I have no clue if this constant type is even ever used!
- public class MethodTypeConstant {
- private final int index;
- private MethodTypeConstant (int i) {
- this.index = i;
- }
- public static MethodHandleConstant parse (byte[] data) {
- return new MethodHandleConstant((((short) data[0]) << 8) | data[1]);
- }
- public Object get (List<Constant> constants) {
- return constants.get(this.index).get(constants).toString();
- }
- }
- // This one still confuses me.
- public class DynamicMethodConstant {
- private final int methodIndex;
- private final int dataIndex;
- private MethodTypeConstant (int i, int i2) {
- this.methodIndex = i;
- this.dataIndex = i2;
- }
- public static MethodHandleConstant parse (byte[] data) {
- return new MethodHandleConstant((((short) data[0]) << 8) | data[1], (((short) data[2]) << 8) | data[3]);
- }
- public Object get (List<Constant> constants) {
- return null;
- }
- public BootstrapMethod get (List<Constant> constants, DecompiledClass clazz) {
- if (!clazz.hasAttribute("BootstrapMethods")) throw new DecompilationException ("Class file missing BootstrapMethods attribute");
- BootstrapMethod method = BootstrapMethod.Attribute.parse(clazz.getAttribute("BootstrapMethods").getRawData(), List<Constant> constants).get(this.methodIndex);
- return method;
- }
- }
- public class BootstrapMethod {
- private final MethodHandle handle;
- private final List<?> args;
- private BootstrapMethod (MethodHandle h, List<?> a) {
- this.handle = h;
- this.args = a;
- }
- public static BootstrapMethod readFrom (InputStream reader, List<Constant> constants) {
- MethodHandle handle = (MethodHandle)constants.get((stream.read() << 8) | stream.read()).get(constants);
- List<?> args = List.<?>of();
- int amt = (stream.read() << 8) | stream.read();
- for (int i = 0; i <= amt; i++)
- args.add(constants.get((stream.read() << 8) | stream.read()).get(constants));
- return new BootstrapMethod(handle, args);
- }
- public static class Attribute {
- private List<BootstrapMethod> methods;
- private Attribute (List<BootstrapMethod> method) {
- this.methods = method;
- }
- public Attribute parse (byte[] data, List<Constant> constants) {
- ByteArrayInputStream stream = new ByteArrayInputStream(data);
- stream.skip(6);
- List<BootstrapMethod> methods = List.<BootstrapMethod>of();
- int amt = (stream.read() << 8) | stream.read();
- for (int i = 0; i <= amt; i++)
- methods.add(BootstrapMethod.readFrom(stream, constants));
- return new Attribute(methods);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement