All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.netty.core.util.JavaClassFile Maven / Gradle / Ivy

The newest version!
package com.github.netty.core.util;

import java.io.*;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * java类文件
 * 

* ClassFile { * u4 magic; * u2 minor_version; * u2 major_version; * u2 constant_pool_count; * cp_info constant_pool[constant_pool_count-1]; * u2 access_flags; * u2 this_class; * u2 super_class; * u2 interfaces_count; * u2 interfaces[interfaces_count]; * u2 fields_count; * field_info fields[fields_count]; * u2 methods_count; * method_info methods[methods_count]; * u2 attributes_count; * attribute_info attributes[attributes_count]; * } *

* class文件结构.官方文档 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html * 参考文件 com.sun.org.apache.bcel.internal.classfile.ClassParser * * @author wangzihao */ public class JavaClassFile { private static final Attribute[] EMPTY_ATTRIBUTES = {}; private static final Attribute.CodeException[] EMPTY_CODE_EXCEPTIONS = {}; private static final Attribute.LineNumber[] EMPTY_LINE_NUMBER_TABLE = {}; private static final Attribute.LocalVariable[] EMPTY_LOCAL_VARIABLE_TABLE = {}; private static final Attribute.InnerClass[] EMPTY_INNER_CLASSES = {}; private static final Attribute.StackMapEntry[] EMPTY_STACK_MAP_ENTRY = {}; private static final Attribute.StackMapFrame[] EMPTY_STACK_MAP_FRAME = {}; private static final Attribute.BootstrapMethod[] EMPTY_BOOT_STRAP_METHOD = {}; private static final Attribute.StackMapType[] EMPTY_STACK_MAP_TYPE = {}; private static final Attribute.MethodParameter[] EMPTY_METHOD_PARAMETER = {}; private static final int[] EMPTY_EXCEPTION_INDEX_TABLE = {}; private static final String[] EMPTY_STRING = {}; private static final Member.Type[] EMPTY_TYPE = {}; private long minorVersion; private JavaVersion majorVersion; private ConstantPool constantPool; private int accessFlags; private int thisClassIndex; private int superClassIndex; private int[] interfacesIndex; private Member[] fields; private Member[] methods; private Attribute[] attributes; private Class clazz; public JavaClassFile(Class clazz) throws ClassNotFoundException, IOException, IllegalClassFormatException { this(new ClassReader(clazz)); this.clazz = clazz; } public JavaClassFile(String path, String name) throws ClassNotFoundException, IOException, IllegalClassFormatException { this(new ClassReader(path, name)); } public JavaClassFile(InputStream in) throws IOException, IllegalClassFormatException { this(new ClassReader(in)); } public JavaClassFile(byte[] codes) throws IllegalClassFormatException { this(new ClassReader(codes)); } public JavaClassFile(ClassReader reader) throws IllegalClassFormatException { int magic = reader.readInt32(); //第一位必须是 cafe babe if (magic != 0xCAFEBABE) { throw new IllegalClassFormatException("is not a Java .class file"); } this.minorVersion = reader.readUint16(); this.majorVersion = JavaVersion.valueOf(reader.readUint16()); this.constantPool = readConstantPool(reader); this.accessFlags = readAccessFlags(reader); this.thisClassIndex = reader.readUint16(); this.superClassIndex = reader.readUint16(); this.interfacesIndex = reader.readUint16s(); this.fields = readMembers(reader, false); this.methods = readMembers(reader, true); this.attributes = readAttributes(reader, null); reader.close(); } /** * Determine the name of the class file, relative to the containing * package: e.g. "String.class" * * @param clazz the class * @return the file name of the ".class" file */ public static String getClassFileName(Class clazz) { Objects.requireNonNull(clazz, "Class must not be null"); String className = clazz.getName(); int lastDotIndex = className.lastIndexOf('.'); return className.substring(lastDotIndex + 1) + ".class"; } private static String toJsonArray(Object array) { if (array == null) { return "null"; } if (array instanceof Object[]) { StringJoiner joiner = new StringJoiner(",", "[", "]"); for (Object e : (Object[]) array) { if (e instanceof Number) { joiner.add(e.toString()); } else if (e == null) { joiner.add("null"); } else if (e.getClass().isArray()) { joiner.add(toJsonArray(array)); } else if (e instanceof CharSequence) { joiner.add("\"" + e + "\""); } else { joiner.add(e.toString()); } } return joiner.toString(); } if (array instanceof byte[]) { return Arrays.toString((byte[]) array); } if (array instanceof int[]) { return Arrays.toString((int[]) array); } if (array instanceof short[]) { return Arrays.toString((short[]) array); } if (array instanceof long[]) { return Arrays.toString((long[]) array); } if (array instanceof double[]) { return Arrays.toString((double[]) array); } return array.toString(); } public static void main(String[] args) throws Exception { JavaClassFile classFile = new JavaClassFile(LinkedHashMap.class); //这里换成自己的class包路径 String path = "D:\\java\\github\\spring-boot-protocol\\target\\classes\\com\\github\\netty\\protocol\\servlet"; Map javaClassMap = new HashMap<>(); File[] files = new File(path).listFiles(); if (files != null) { for (File file : files) { String fileName = file.getName(); if (fileName.endsWith(".class")) { JavaClassFile javaClassFile = new JavaClassFile(path, fileName); List localVariables = Stream.of(javaClassFile.getMethods()).map(Member::getLocalVariableTable).collect(Collectors.toList()); javaClassMap.put(fileName, javaClassFile); } } } System.out.println("end.."); } private int readAccessFlags(ClassReader reader) { int accessFlags = reader.readUint16(); if ((accessFlags & Modifier.INTERFACE) != 0) { accessFlags |= Modifier.ABSTRACT; } return accessFlags; } private ConstantPool readConstantPool(ClassReader reader) { int constantPoolCount = reader.readUint16(); return new ConstantPool(constantPoolCount, reader); } private Member[] readMembers(ClassReader reader, boolean method) { int memberCount = reader.readUint16(); Member[] members = new Member[memberCount]; for (int i = 0; i < members.length; i++) { members[i] = new Member(); members[i].method = method; members[i].classFile = this; members[i].accessFlags = reader.readUint16(); members[i].nameIndex = reader.readUint16(); members[i].descriptorIndex = reader.readUint16(); members[i].name = constantPool.getUtf8(members[i].nameIndex); members[i].descriptorName = constantPool.getUtf8(members[i].descriptorIndex); members[i].attributes = readAttributes(reader, null); } return members; } private Attribute[] readAttributes(ClassReader reader, Attribute parent) { int attributesCount = reader.readUint16(); if (attributesCount == 0) { return EMPTY_ATTRIBUTES; } else { Attribute[] attributes = new Attribute[attributesCount]; for (int i = 0; i < attributes.length; i++) { int attrNameIndex = reader.readUint16(); attributes[i] = new Attribute(attrNameIndex, reader.readInt32(), parent, reader); } return attributes; } } public JavaVersion getMajorVersion() { return majorVersion; } public Attribute[] getAttributes() { return attributes; } public ConstantPool getConstantPool() { return constantPool; } public int getAccessFlags() { return accessFlags; } public Member[] getFields() { return fields; } public Member[] getMethods() { return methods; } public Member.Type getThisType() { return Member.Type.getObjectType(getThisClassName()); } public Class getThisClass() { if (clazz == null) { clazz = getThisType().resolveClass(); } return clazz; } public String getThisClassName() { return constantPool.getClassName(thisClassIndex); } public String getSuperClassName() { if (superClassIndex != 0) { return constantPool.getClassName(superClassIndex); } return ""; } public JavaClassFile getSuperClassFile() throws IllegalClassFormatException, IOException, ClassNotFoundException { if (superClassIndex != 0) { return new JavaClassFile(getSuperClass()); } return null; } public Member.Type getSuperType() { if (superClassIndex != 0) { return Member.Type.getObjectType(getSuperClassName()); } else { return null; } } public Class getSuperClass() { if (superClassIndex != 0) { return getSuperType().resolveClass(); } else { return null; } } public String[] getInterfaceNames() { String[] interfaceNames = new String[interfacesIndex.length]; for (int i = 0; i < interfaceNames.length; i++) { interfaceNames[i] = constantPool.getClassName(interfacesIndex[i]); } return interfaceNames; } public Member.Type[] getInterfaceTypes() { String[] interfaceNames = getInterfaceNames(); Member.Type[] types = new Member.Type[interfaceNames.length]; for (int i = 0; i < interfaceNames.length; i++) { types[i] = Member.Type.getObjectType(interfaceNames[i]); } return types; } public Class[] getInterfaceClasses() { String[] interfaceNames = getInterfaceNames(); Class[] types = new Class[interfaceNames.length]; for (int i = 0; i < interfaceNames.length; i++) { types[i] = Member.Type.getObjectType(interfaceNames[i]).resolveClass(); } return types; } public Member getMethod(String methodName, Class[] parameterTypes, Class returnType) { String methodDescriptor = Member.Type.getMethodDescriptor(parameterTypes, returnType); for (Member method : methods) { if (methodName.equals(method.getName()) && methodDescriptor.equals(method.getDescriptorName())) { return method; } } return null; } public List getLocalVariableTableList() { return Stream.of(getMethods()).map(Member::getLocalVariableTable).collect(Collectors.toList()); } public boolean isInterface() { return Modifier.isInterface(accessFlags); } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"majorVersion\":\"" + majorVersion + "\"") .add("\"minorVersion\":" + minorVersion) .add("\"accessFlags\":\"" + Modifier.toString(accessFlags) + "\"") .add("\"thisClassIndex\":" + thisClassIndex) .add("\"thisClassName\":\"" + getThisClassName() + "\"") .add("\"superClassIndex\":" + superClassIndex) .add("\"superClassName\":\"" + getSuperClassName() + "\"") .add("\"interfaces\":" + toJsonArray(getInterfaceNames())) .add("\"fields\":" + toJsonArray(getFields())) .add("\"methods\":" + toJsonArray(getMethods())) .add("\"attributes\":" + toJsonArray(attributes)) .add("\"constantPool\":" + toJsonArray(constantPool.constants)) .add("\"constantPoolDataLength\":" + Arrays.stream(constantPool.constants) .filter(Objects::nonNull) .mapToInt(ConstantPool.ConstantInfo::length) .sum()) .toString(); } public enum JavaVersion { /** * Java ClassFile versions (the minor version is stored in the 16 most * significant bits, and the * major version in the 16 least significant bits). */ V1_1(3 << 16 | 45), V1_2(0 << 16 | 46), V1_3(0 << 16 | 47), V1_4(0 << 16 | 48), V1_5(0 << 16 | 49), V1_6(0 << 16 | 50), V1_7(0 << 16 | 51), V1_8(0 << 16 | 52), V9(0 << 16 | 53), V10(0 << 16 | 54), V11(0 << 16 | 55), V12(0 << 16 | 56); private long major; JavaVersion(long major) { this.major = major; } public static JavaVersion valueOf(long major) { for (JavaVersion version : values()) { if (version.major == major) { return version; } } throw new IllegalArgumentException("bad major"); } public long getMajor() { return major; } } public static class ConstantPool { private ConstantInfo[] constants; public ConstantPool(int constantPoolCount, ClassReader reader) { constants = new ConstantInfo[constantPoolCount]; // The constant_pool table is indexed from 1 to constant_pool_count - 1. for (int i = 1; i < constantPoolCount; i++) { ConstantInfo constantInfo = readConstantInfo(i, reader); constants[i] = constantInfo; // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.5 // All 8-byte constants take up two entries in the constant_pool table of the class file. // If a CONSTANT_Long_info or CONSTANT_Double_info structure is the item in the constant_pool // table at index n, then the next usable item in the pool is located at index n+2. // The constant_pool index n+1 must be valid but is considered unusable. if (constantInfo instanceof ConstantDoubleInfo || constantInfo instanceof ConstantLongInfo) { i++; } } } private ConstantInfo readConstantInfo(int index, ClassReader reader) { int tag = reader.readUint8(); ConstantInfo constantInfo; switch (tag) { case ConstantInfo.CONSTANT_INTEGER: constantInfo = new ConstantIntegerInfo(index, reader); break; case ConstantInfo.CONSTANT_FLOAT: constantInfo = new ConstantFloatInfo(index, reader); break; case ConstantInfo.CONSTANT_LONG: constantInfo = new ConstantLongInfo(index, reader); break; case ConstantInfo.CONSTANT_DOUBLE: constantInfo = new ConstantDoubleInfo(index, reader); break; case ConstantInfo.CONSTANT_UTF8: constantInfo = new ConstantUtf8Info(index, reader); break; case ConstantInfo.CONSTANT_STRING: constantInfo = new ConstantStringInfo(index, reader); break; case ConstantInfo.CONSTANT_CLASS: constantInfo = new ConstantClassInfo(index, reader); break; case ConstantInfo.CONSTANT_FIELD_REF: constantInfo = new ConstantFieldRefInfo(index, new ConstantMemberRefInfo(reader)); break; case ConstantInfo.CONSTANT_METHOD_REF: constantInfo = new ConstantMethodRefInfo(index, new ConstantMemberRefInfo(reader)); break; case ConstantInfo.CONSTANT_INTERFACE_METHOD_REF: constantInfo = new ConstantInterfaceMethodRefInfo(index, new ConstantMemberRefInfo(reader)); break; case ConstantInfo.CONSTANT_NAME_AND_TYPE: constantInfo = new ConstantNameAndTypeInfo(index, reader); break; case ConstantInfo.CONSTANT_METHOD_HANDLE: constantInfo = new ConstantMethodHandleInfo(index, reader); break; case ConstantInfo.CONSTANT_INVOKE_DYNAMIC: constantInfo = new ConstantInvokeDynamicInfo(index, reader); break; case ConstantInfo.CONSTANT_METHOD_TYPE: constantInfo = new ConstantMethodTypeInfo(index, reader); break; default: { //用户可以自定义解析器 System.out.println("Unkown constant pool tag: " + tag); constantInfo = new ConstantUnkownInfo(index, tag); break; } } return constantInfo; } public ConstantMethodHandleInfo getConstantMethodHandleInfo(int index) { return (ConstantMethodHandleInfo) getConstantInfo(index); } public ConstantInfo getConstantInfo(int index) { return constants[index]; } public ConstantNameAndTypeInfo getNameAndType(int index) { return (ConstantNameAndTypeInfo) getConstantInfo(index); } public String getClassName(int index) { return getUtf8(((ConstantClassInfo) getConstantInfo(index)).nameIndex); } public String getUtf8(int stringIndex) { if (stringIndex == 0) { return null; } return ((ConstantUtf8Info) getConstantInfo(stringIndex)).value(); } public String getClassNameForToString(int index) { if (index == 0) { return null; } return getUtf8ForToString(((ConstantClassInfo) getConstantInfo(index)).nameIndex); } public String getUtf8ForToString(int stringIndex) { if (stringIndex == 0) { return null; } return ((ConstantUtf8Info) getConstantInfo(stringIndex)).valueToString(); } public int getInteger(int index) { return ((ConstantIntegerInfo) getConstantInfo(index)).value(); } public double getDouble(int index) { return ((ConstantDoubleInfo) getConstantInfo(index)).value(); } public int getFloat(int index) { return ((ConstantFloatInfo) getConstantInfo(index)).value(); } public long getLong(int index) { return ((ConstantLongInfo) getConstantInfo(index)).value(); } public interface ConstantInfo { int CONSTANT_UTF8 = 1; int CONSTANT_INTEGER = 3; int CONSTANT_FLOAT = 4; int CONSTANT_LONG = 5; int CONSTANT_DOUBLE = 6; int CONSTANT_CLASS = 7; int CONSTANT_STRING = 8; int CONSTANT_FIELD_REF = 9; int CONSTANT_METHOD_REF = 10; int CONSTANT_INTERFACE_METHOD_REF = 11; int CONSTANT_NAME_AND_TYPE = 12; int CONSTANT_METHOD_HANDLE = 15; int CONSTANT_METHOD_TYPE = 16; int CONSTANT_INVOKE_DYNAMIC = 18; String name(); int length(); } public class ConstantStringInfo implements ConstantInfo { private int stringIndex; private int index; public ConstantStringInfo(int index, ClassReader reader) { this.index = index; this.stringIndex = reader.readUint16(); } public String value() { return getUtf8(stringIndex); } @Override public String name() { return "String"; } @Override public int length() { return 2; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"stringIndex\":" + stringIndex) .add("\"value\":\"" + getUtf8ForToString(stringIndex) + "\"") .toString(); } } public class ConstantDoubleInfo implements ConstantInfo { private byte[] value; private int index; public ConstantDoubleInfo(int index, ClassReader reader) { this.index = index; value = reader.readInt8s(8); } public double value() { long data = ((long) value[0] & 0xff) << 56 | ((long) value[1] & 0xff) << 48 | ((long) value[2] & 0xff) << 40 | ((long) value[3] & 0xff) << 32 | ((long) value[4] & 0xff) << 24 | ((long) value[5] & 0xff) << 16 | ((long) value[6] & 0xff) << 8 | (long) value[7] & 0xff; return Double.longBitsToDouble(data); } @Override public String name() { return "Double"; } @Override public int length() { return 8; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"value\":" + toJsonArray(value)) .toString(); } } public class ConstantIntegerInfo implements ConstantInfo { private int value; private int index; public ConstantIntegerInfo(int index, ClassReader reader) { this.index = index; this.value = reader.readInt32(); } @Override public String name() { return "Integer"; } public int value() { return value; } @Override public int length() { return 4; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"value\":" + value) .toString(); } } public class ConstantFloatInfo implements ConstantInfo { private int value; private int index; public ConstantFloatInfo(int index, ClassReader reader) { this.index = index; value = reader.readInt32(); } public int value() { return value; } @Override public String name() { return "Float"; } @Override public int length() { return 4; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"value\":" + value) .toString(); } } public class ConstantLongInfo implements ConstantInfo { private byte[] value; private int index; public ConstantLongInfo(int index, ClassReader reader) { this.index = index; value = reader.readInt8s(8); } public long value() { long data = ((long) value[0] & 0xff) << 56 | ((long) value[1] & 0xff) << 48 | ((long) value[2] & 0xff) << 40 | ((long) value[3] & 0xff) << 32 | ((long) value[4] & 0xff) << 24 | ((long) value[5] & 0xff) << 16 | ((long) value[6] & 0xff) << 8 | (long) value[7] & 0xff; return data; } @Override public String name() { return "Long"; } @Override public int length() { return 8; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"value\":" + toJsonArray(value)) .toString(); } } public class ConstantUtf8Info implements ConstantInfo { private String value; private String valueToString; private int length; private int index; public ConstantUtf8Info(int index, ClassReader reader) { this.index = index; length = reader.readUint16(); byte[] bytes = reader.readInt8s(length); value = new String(bytes, Charset.forName("UTF-8")); } public String value() { return value; } private String valueToString() { if (valueToString == null) { valueToString = value.replace("\"", "\\\"") .replace(":", "\\:") .replace("{", "\\{") .replace("}", "\\}") .replace("[", "\\[") .replace("]", "\\]"); } return valueToString; } @Override public String name() { return "UTF8"; } @Override public int length() { return length; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"value\":\"" + valueToString() + "\"") .toString(); } } public class ConstantClassInfo implements ConstantInfo { private int nameIndex; private int index; public ConstantClassInfo(int index, ClassReader reader) { this.index = index; this.nameIndex = reader.readUint16(); } public String value() { return getUtf8(nameIndex); } @Override public String name() { return "Class"; } @Override public int length() { return 2; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"nameIndex\":" + nameIndex) .add("\"name\":\"" + getUtf8ForToString(nameIndex) + "\"") .toString(); } } public class ConstantFieldRefInfo implements ConstantInfo { private ConstantMemberRefInfo memberRefInfo; private int index; public ConstantFieldRefInfo(int index, ConstantMemberRefInfo memberRefInfo) { this.index = index; this.memberRefInfo = memberRefInfo; } public ConstantMemberRefInfo value() { return memberRefInfo; } @Override public String name() { return "FieldRef"; } @Override public int length() { return 0; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"memberRef\":" + memberRefInfo) .toString(); } } public class ConstantMemberRefInfo implements ConstantInfo { private int classIndex; private int nameAndTypeIndex; public ConstantMemberRefInfo(ClassReader reader) { classIndex = reader.readUint16(); nameAndTypeIndex = reader.readUint16(); } public String className() { return getClassName(classIndex); } public ConstantNameAndTypeInfo nameAndType() { return getNameAndType(nameAndTypeIndex); } @Override public String name() { return "MemberRef"; } @Override public int length() { return 4; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"classIndex\":" + classIndex) .add("\"nameAndTypeIndex\":" + nameAndTypeIndex) .add("\"class\":\"" + className() + "\"") .add("\"nameAndType\":" + nameAndType()) .toString(); } } public class ConstantMethodRefInfo implements ConstantInfo { private ConstantMemberRefInfo memberRefInfo; private int index; public ConstantMethodRefInfo(int index, ConstantMemberRefInfo memberRefInfo) { this.index = index; this.memberRefInfo = memberRefInfo; } @Override public String name() { return "MethodRef"; } @Override public int length() { return 0; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"memberRef\":" + memberRefInfo) .toString(); } } public class ConstantInterfaceMethodRefInfo implements ConstantInfo { private ConstantMemberRefInfo memberRefInfo; private int index; public ConstantInterfaceMethodRefInfo(int index, ConstantMemberRefInfo memberRefInfo) { this.index = index; this.memberRefInfo = memberRefInfo; } @Override public String name() { return "InterfaceMethodRef"; } @Override public int length() { return 0; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"memberRef\":" + memberRefInfo) .toString(); } } public class ConstantNameAndTypeInfo implements ConstantInfo { private int nameIndex; private int descriptorIndex; private int index; public ConstantNameAndTypeInfo(int index, ClassReader reader) { nameIndex = reader.readUint16(); this.index = index; descriptorIndex = reader.readUint16(); } @Override public String name() { return "NameAndType"; } @Override public int length() { return 4; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"name\":\"" + getUtf8ForToString(nameIndex) + "\"") .add("\"descriptor\":\"" + getUtf8ForToString(descriptorIndex) + "\"") .add("\"nameIndex\":" + nameIndex) .add("\"descriptorIndex\":" + descriptorIndex) .toString(); } } public class ConstantMethodTypeInfo implements ConstantInfo { private int descriptorIndex; private int index; public ConstantMethodTypeInfo(int index, ClassReader reader) { this.index = index; descriptorIndex = reader.readUint16(); } @Override public String name() { return "MethodType"; } @Override public int length() { return 2; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"descriptorIndex\":" + descriptorIndex) .add("\"descriptor\":\"" + getUtf8ForToString(descriptorIndex) + "\"") .toString(); } } public class ConstantMethodHandleInfo implements ConstantInfo { private int referenceKind; private int referenceIndex; private int index; public ConstantMethodHandleInfo(int index, ClassReader reader) { this.index = index; referenceKind = reader.readUint8(); referenceIndex = reader.readUint16(); } @Override public String name() { return "MethodHandle"; } @Override public int length() { return 6; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"referenceKind\":\"" + Opcodes.METHOD_HANDLES_NAMES[referenceKind] + "\"") .add("\"referenceIndex\":" + referenceIndex) .add("\"reference\":" + getConstantInfo(referenceIndex)) .toString(); } } public class ConstantInvokeDynamicInfo implements ConstantInfo { private int bootstrapMethodAttrIndex; private int nameAndTypeIndex; private int index; public ConstantInvokeDynamicInfo(int index, ClassReader reader) { this.index = index; bootstrapMethodAttrIndex = reader.readUint16(); nameAndTypeIndex = reader.readUint16(); } public ConstantNameAndTypeInfo nameAndType() { return getNameAndType(nameAndTypeIndex); } @Override public String name() { return "InvokeDynamic"; } @Override public int length() { return 8; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"bootstrapMethodAttrIndex\":" + bootstrapMethodAttrIndex) .add("\"nameAndTypeIndex\":" + nameAndTypeIndex) .add("\"nameAndType\":" + nameAndType()) .toString(); } } public class ConstantUnkownInfo implements ConstantInfo { private int tag; private int index; public ConstantUnkownInfo(int index, int tag) { this.index = index; this.tag = tag; } @Override public String name() { return "Unkown"; } @Override public int length() { return 0; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"constant\":\"" + name() + "\"") .add("\"length\":" + length()) .add("\"tag\":" + tag) .toString(); } } } public static class StaticConstructor implements java.lang.reflect.Member { private static final int ACCESS_MODIFIERS = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE; private final Member member; public StaticConstructor(Member member) { this.member = member; } @Override public Class getDeclaringClass() { return member.classFile.getThisClass(); } @Override public String getName() { return member.name; } @Override public int getModifiers() { return member.accessFlags; } @Override public boolean isSynthetic() { return (member.accessFlags & 0x00001000) != 0; } @Override public String toString() { try { StringBuilder sb = new StringBuilder(); boolean isDefault = member.isDefaultMethod(); int mod = getModifiers(); if (mod != 0 && !isDefault) { sb.append(Modifier.toString(mod)).append(' '); } else { int access_mod = mod & ACCESS_MODIFIERS; if (access_mod != 0) { sb.append(Modifier.toString(access_mod)).append(' '); } if (isDefault) { sb.append("default "); } mod = (mod & ~ACCESS_MODIFIERS); if (mod != 0) { sb.append(Modifier.toString(mod)).append(' '); } } sb.append(getDeclaringClass().getTypeName()); sb.append('('); Member.Type[] types = member.getMethodArgumentTypes(); for (int j = 0; j < types.length; j++) { sb.append(types[j].getClassName()); if (j < (types.length - 1)) { sb.append(","); } } sb.append(')'); return sb.toString(); } catch (Exception e) { return "<" + e + ">"; } } } public static class Parameter{ private Member.Type type; private String name; public Member.Type getType() { return type; } public String getName() { return name; } @Override public String toString() { return name; } } public static class Member { private boolean method; private int accessFlags; private int nameIndex; private int descriptorIndex; private String name; private String descriptorName; private Attribute[] attributes; private Class[] javaArgumentTypes; private Type[] argumentTypes; private String[] parameterNames; private Parameter[] parameters; private Attribute.MethodParameter[] methodParameters; /** * 局部变量, List, 这里存的是 List */ private Attribute.LocalVariable[] localVariables; /** * 局部变量的泛型 List, 这里存的是 ItemType */ private Attribute.LocalVariable[] localVariablesType; private JavaClassFile classFile; public int getAccessFlags() { return accessFlags; } /** * 获取入参在局部变量表的位置 * * @return 局部变量表所在下标的数组 */ public int[] getArgumentLocalVariableTableIndex() { Type[] argumentTypes = getMethodArgumentTypes(); int[] lvtIndex = new int[argumentTypes.length]; int nextIndex = Modifier.isStatic(accessFlags) ? 0 : 1;//静态没有this for (int i = 0; i < argumentTypes.length; i++) { lvtIndex[i] = nextIndex; if (argumentTypes[i] == Type.LONG_TYPE || argumentTypes[i] == Type.DOUBLE_TYPE) { nextIndex += 2; } else { nextIndex++; } } return lvtIndex; } public boolean isDefaultMethod() { // Default methods are public non-abstract instance methods // declared in an interface. return method && ((accessFlags & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC) && classFile.isInterface(); } public boolean isStatic() { return Modifier.isStatic(accessFlags); } public boolean isField() { return !method; } public boolean isMethod() { return method; } public boolean isConstructor() { return method && "".equals(name); } public boolean isStaticConstructor() { return method && isStatic() && "".equals(name); } public Type getFieldType() { if (isField()) { return Type.getType(getDescriptorName()); } return null; } public Type getMethodReturnType() { if (isMethod()) { return Type.getReturnType(getDescriptorName()); } return null; } public String getSignature() { for (Attribute attribute : attributes) { if (attribute.isSignature()) { ConstantPool.ConstantUtf8Info utf8Info = (ConstantPool.ConstantUtf8Info) attribute.get("signature"); return utf8Info.value(); } } return null; } public Type getSignatureType() { for (Attribute attribute : attributes) { if (attribute.isSignature()) { String signatureType = (String) attribute.get("signatureType"); return signatureType != null ? Type.getType(signatureType) : null; } } return null; } public Class getSignatureClass() { Type signatureType = getSignatureType(); return signatureType == null ? null : signatureType.resolveClass(); } /** * 获取泛型 多个嵌套那种复杂的用这个{@link #getMethodArgumentTypes}, 目前只支持单个泛型 * 自己想实现可以用原始数据自己解析 {@link #getSignature()} * * @return Type */ public Type getSignatureGenericType() { for (Attribute attribute : attributes) { if (attribute.isSignature()) { String signatureGeneric = (String) attribute.get("signatureGeneric"); return signatureGeneric != null ? Type.getType(signatureGeneric) : null; } } return null; } public Class getSignatureGenericClass() { Type genericType = getSignatureGenericType(); return genericType == null ? null : genericType.resolveClass(); } public Class getMethodReturnClass() { if (isMethod()) { return getMethodReturnType().resolveClass(); } return null; } public Class getFieldClass() { if (isField()) { return getFieldType().resolveClass(); } return null; } public Type[] getMethodArgumentTypes() { if (argumentTypes == null) { String signature = getSignature(); if (signature != null) { int open = signature.indexOf('('); int close = signature.lastIndexOf(')'); String substring = signature.substring(open == -1 ? 0 : open, close == -1 ? signature.length() : close + 1); argumentTypes = Type.getArgumentTypes(substring); } else if (isMethod()) { argumentTypes = Type.getArgumentTypes(getDescriptorName()); } } return argumentTypes; } public Class[] getMethodArgumentClasses() { if (javaArgumentTypes == null) { Type[] argumentTypes = getMethodArgumentTypes(); Class[] javaArgumentTypes = new Class[argumentTypes.length]; for (int i = 0; i < argumentTypes.length; i++) { javaArgumentTypes[i] = argumentTypes[i].resolveClass(); } this.javaArgumentTypes = javaArgumentTypes; } return javaArgumentTypes; } public java.lang.reflect.Member toJavaMember() { Class target = classFile.getThisClass(); if (isMethod()) { Class[] argumentTypes = getMethodArgumentClasses(); try { if (isConstructor()) { return target.getDeclaredConstructor(argumentTypes); } else if (isStaticConstructor()) { return new StaticConstructor(this); } else if (isStatic()) { return target.getMethod(name, argumentTypes); } else { return target.getDeclaredMethod(name, argumentTypes); } } catch (NoSuchMethodException e) { throw new IllegalStateException("toJavaMember error ", e); } } else { try { return target.getDeclaredField(name); } catch (NoSuchFieldException e) { throw new IllegalStateException("toJavaMember error ", e); } } } public Parameter[] getParameters() { if (this.parameters == null) { String[] parameterNames = getParameterNames(); Type[] methodArgumentTypes = getMethodArgumentTypes(); Parameter[] parameters = new Parameter[parameterNames.length]; for (int i = 0; i < parameters.length; i++) { parameters[i] = new Parameter(); parameters[i].name = parameterNames[i]; parameters[i].type = methodArgumentTypes[i]; } this.parameters = parameters; } return parameters; } public String[] getParameterNames() { if (this.parameterNames == null) { String[] parameterNames; //获取入参在局部变量表的位置 int[] lvtIndices = getArgumentLocalVariableTableIndex(); if (lvtIndices.length == 0) { parameterNames = EMPTY_STRING; } else { Attribute.LocalVariable[] localVariableTable = getLocalVariableTable(); if (localVariableTable == null || localVariableTable.length == 0) { parameterNames = EMPTY_STRING; } else { parameterNames = new String[lvtIndices.length]; //变量局部变量表 for (int i = 0; i < localVariableTable.length; i++) { int localVariableIndex = localVariableTable[i].index(); //根据入参位置,寻找方法入参的变量名称 for (int j = 0; j < lvtIndices.length; j++) { if (localVariableIndex == lvtIndices[j]) { parameterNames[j] = localVariableTable[i].name(); break; } } } } } this.parameterNames = parameterNames; } return this.parameterNames; } public String getName() { return name; } public String getDescriptorName() { return descriptorName; } public Attribute.LocalVariable[] getLocalVariableTable() { if (localVariables == null) { localVariables = findLocalVariable(); } return localVariables; } public Attribute.LocalVariable[] getLocalVariableTypeTable() { if (localVariablesType == null) { localVariablesType = findLocalVariableTypeTable(); } return localVariablesType; } public Attribute.MethodParameter[] getMethodParameters() { if (methodParameters == null) { methodParameters = findMethodParameters(); } return methodParameters; } public Opcodes getOpcodes() { if (this.attributes != null) { for (Attribute attributeInfo : this.attributes) { if (attributeInfo.isAttrCode()) { return attributeInfo.getOpcodes(); } } } return null; } public Attribute.CodeException[] getExceptionTable() { if (this.attributes != null) { for (Attribute attributeInfo : this.attributes) { if (attributeInfo.isAttrCode()) { return (Attribute.CodeException[]) attributeInfo.get("exceptionTable"); } } } return null; } public Integer getMaxStack() { if (this.attributes != null) { for (Attribute attributeInfo : this.attributes) { if (attributeInfo.isAttrCode()) { return (Integer) attributeInfo.get("maxStack"); } } } return null; } public Integer getMaxLocals() { if (this.attributes != null) { for (Attribute attributeInfo : this.attributes) { if (attributeInfo.isAttrCode()) { return (Integer) attributeInfo.get("maxLocals"); } } } return null; } public Attribute.LineNumber[] getLineNumberTable() { if (this.attributes != null) { for (Attribute attributeInfo : this.attributes) { if (!attributeInfo.isAttrCode()) { continue; } Attribute[] codeAttributes = attributeInfo.attributes(); for (Attribute codeAttributeInfo : codeAttributes) { if (codeAttributeInfo.isLineNumberTable()) { return (Attribute.LineNumber[]) codeAttributeInfo.get("lineNumberTable"); } } } } return null; } public Attribute.StackMapFrame[] getStackMapTable() { if (this.attributes != null) { for (Attribute attributeInfo : this.attributes) { if (!attributeInfo.isAttrCode()) { continue; } Attribute[] codeAttributes = attributeInfo.attributes(); for (Attribute codeAttributeInfo : codeAttributes) { if (codeAttributeInfo.isStackMapTable()) { return (Attribute.StackMapFrame[]) codeAttributeInfo.get("entries"); } } } } return null; } public Attribute.Annotation[] getRuntimeVisibleAnnotations() { if (this.attributes != null) { for (Attribute attributeInfo : this.attributes) { if (attributeInfo.isRuntimeVisibleAnnotations()) { return (Attribute.Annotation[]) attributeInfo.get("annotations"); } } } return null; } private Attribute.LocalVariable[] findLocalVariable() { if (this.attributes == null) { return EMPTY_LOCAL_VARIABLE_TABLE; } for (Attribute attributeInfo : this.attributes) { if (!attributeInfo.isAttrCode()) { continue; } Attribute[] codeAttributes = attributeInfo.attributes(); for (Attribute codeAttributeInfo : codeAttributes) { if (codeAttributeInfo.isAttrLocalVariableTable()) { return codeAttributeInfo.localVariableTable(); } } return EMPTY_LOCAL_VARIABLE_TABLE; } return EMPTY_LOCAL_VARIABLE_TABLE; } private Attribute.LocalVariable[] findLocalVariableTypeTable() { if (this.attributes == null) { return EMPTY_LOCAL_VARIABLE_TABLE; } for (Attribute attributeInfo : this.attributes) { if (!attributeInfo.isAttrCode()) { continue; } Attribute[] codeAttributes = attributeInfo.attributes(); for (Attribute codeAttributeInfo : codeAttributes) { if (codeAttributeInfo.isAttrLocalVariableTypeTable()) { return codeAttributeInfo.localVariableTable(); } } return EMPTY_LOCAL_VARIABLE_TABLE; } return EMPTY_LOCAL_VARIABLE_TABLE; } private Attribute.MethodParameter[] findMethodParameters() { if (this.attributes == null) { return EMPTY_METHOD_PARAMETER; } for (Attribute attributeInfo : this.attributes) { if (attributeInfo.isMethodParameters()) { return attributeInfo.methodParameters(); } } return EMPTY_METHOD_PARAMETER; } @Override public String toString() { StringJoiner joiner = new StringJoiner(",", "{", "}"); joiner.add("\"accessFlags\":\"" + Modifier.toString(accessFlags) + "\""); joiner.add("\"name\":\"" + getName() + "\""); joiner.add("\"getDescriptorName\":\"" + getDescriptorName() + "\""); joiner.add("\"attributes\":" + toJsonArray(attributes)); return joiner.toString(); } public static final class Type { /** * The sort of the {@code void} type. See {@link #getSort}. */ public static final int VOID = 0; /** * The sort of the {@code boolean} type. See {@link #getSort}. */ public static final int BOOLEAN = 1; /** * The sort of the {@code char} type. See {@link #getSort}. */ public static final int CHAR = 2; /** * The sort of the {@code byte} type. See {@link #getSort}. */ public static final int BYTE = 3; /** * The sort of the {@code short} type. See {@link #getSort}. */ public static final int SHORT = 4; /** * The sort of the {@code int} type. See {@link #getSort}. */ public static final int INT = 5; /** * The sort of the {@code float} type. See {@link #getSort}. */ public static final int FLOAT = 6; /** * The sort of the {@code long} type. See {@link #getSort}. */ public static final int LONG = 7; /** * The sort of the {@code double} type. See {@link #getSort}. */ public static final int DOUBLE = 8; /** * The sort of array reference types. See {@link #getSort}. */ public static final int ARRAY = 9; /** * The sort of object reference types. See {@link #getSort}. */ public static final int OBJECT = 10; /** * The sort of method types. See {@link #getSort}. */ public static final int METHOD = 11; /** * The (private) sort of object reference types represented with an internal name. */ public static final int INTERNAL = 12; /** * The sort of object reference types. See {@link #getSort}. */ public static final int OBJECT_REF = 13; /** * The generic type */ public static final int GENERIC_TYPE_NAME = 14; /** * The descriptors of the primitive types. */ private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD"; /** * The {@code void} type. */ public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1); /** * The {@code boolean} type. */ public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1); /** * The {@code char} type. */ public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1); /** * The {@code byte} type. */ public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1); /** * The {@code short} type. */ public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1); /** * The {@code int} type. */ public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1); /** * The {@code float} type. */ public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1); /** * The {@code long} type. */ public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1); /** * The {@code double} type. */ public static final Type DOUBLE_TYPE = new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1); /** * Suffix for array class names: {@code "[]"}. */ private static final String ARRAY_SUFFIX = "[]"; /** * Prefix for internal array class names: {@code "["}. */ private static final String INTERNAL_ARRAY_PREFIX = "["; /** * Prefix for internal non-primitive array class names: {@code "[L"}. */ private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L"; /** * The package separator character: {@code '.'}. */ private static final char PACKAGE_SEPARATOR = '.'; /** * Map with primitive wrapper type as key and corresponding primitive * type as value, for example: Integer.class -> int.class. */ private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new IdentityHashMap<>(8); /** * Map with primitive type name as key and corresponding primitive * type as value, for example: "int" -> "int.class". */ private static final Map> PRIMITIVE_TYPE_NAME_MAP = new HashMap<>(32); /** * Map with common Java language class name as key and corresponding Class as value. * Primarily for efficient deserialization of remote invocations. */ private static final Map> COMMON_CLASS_CACHE = new HashMap<>(64); static { PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class); PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class); // Map entry iteration is less expensive to initialize than forEach with lambdas for (Map.Entry, Class> entry : PRIMITIVE_WRAPPER_TYPE_MAP.entrySet()) { registerCommonClasses(entry.getKey()); } Set> primitiveTypes = new HashSet<>(32); primitiveTypes.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values()); Collections.addAll(primitiveTypes, boolean[].class, byte[].class, char[].class, double[].class, float[].class, int[].class, long[].class, short[].class); primitiveTypes.add(void.class); for (Class primitiveType : primitiveTypes) { PRIMITIVE_TYPE_NAME_MAP.put(primitiveType.getName(), primitiveType); } registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class, Float[].class, Integer[].class, Long[].class, Short[].class); registerCommonClasses(Number.class, Number[].class, String.class, String[].class, Class.class, Class[].class, Object.class, Object[].class); registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class, Error.class, StackTraceElement.class, StackTraceElement[].class); registerCommonClasses(Enum.class, Iterable.class, Iterator.class, Enumeration.class, Collection.class, List.class, Set.class, Map.class, Map.Entry.class, Optional.class); Class[] javaLanguageInterfaceArray = {Serializable.class, Externalizable.class, Closeable.class, AutoCloseable.class, Cloneable.class, Comparable.class}; registerCommonClasses(javaLanguageInterfaceArray); } /** * The sort of this type. Either {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, * {@link #SHORT}, {@link #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, * {@link #OBJECT}, {@link #METHOD} or {@link #INTERNAL}. */ private final int sort; /** * A buffer containing the value of this field or method type. This value is an internal name for * {@link #OBJECT} and {@link #INTERNAL} types, and a field or method descriptor in the other * cases. * *

For {@link #OBJECT} types, this field also contains the descriptor: the characters in * [{@link #valueBegin},{@link #valueEnd}) contain the internal name, and those in [{@link * #valueBegin} - 1, {@link #valueEnd} + 1) contain the descriptor. */ private final String valueBuffer; /** * The beginning index, inclusive, of the value of this Java field or method type in {@link * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, * and a field or method descriptor in the other cases. */ private final int valueBegin; /** * The end index, exclusive, of the value of this Java field or method type in {@link * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, * and a field or method descriptor in the other cases. */ private final int valueEnd; private static final Type[] EMPTY_GENERIC_TYPES = {}; private Type[] genericTypes = EMPTY_GENERIC_TYPES; // ----------------------------------------------------------------------------------------------- // Fields // ----------------------------------------------------------------------------------------------- /** * Constructs a reference type. * * @param sort the sort of this type, see {@link #sort}. * @param valueBuffer a buffer containing the value of this field or method type. * @param valueBegin the beginning index, inclusive, of the value of this field or method type in * valueBuffer. * @param valueEnd the end index, exclusive, of the value of this field or method type in * valueBuffer. */ private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) { this.sort = sort; this.valueBuffer = valueBuffer; this.valueBegin = valueBegin; this.valueEnd = valueEnd; } public Type[] getGenericTypes() { return genericTypes; } public Type getGenericType(int index) { if (index >= 0 && index < genericTypes.length) { return genericTypes[index]; } else { return null; } } public Class resolveGenericClass(int index) { if (index >= 0 && index < genericTypes.length) { return genericTypes[index].resolveClass(); } else { return null; } } private void addGenericTypes(Type type) { genericTypes = Arrays.copyOf(genericTypes, genericTypes.length + 1); genericTypes[genericTypes.length - 1] = type; } private static Class resolvePrimitiveClassName(String name) { Class result = null; // Most class names will be quite long, considering that they // SHOULD sit in a package, so a length check is worthwhile. if (name != null && name.length() <= 8) { // Could be a primitive - likely. result = PRIMITIVE_TYPE_NAME_MAP.get(name); } return result; } public static Class forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { Class clazz = resolvePrimitiveClassName(name); if (clazz == null) { clazz = COMMON_CLASS_CACHE.get(name); } if (clazz != null) { return clazz; } // "java.lang.String[]" style arrays if (name.endsWith(ARRAY_SUFFIX)) { String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); Class elementClass = forName(elementClassName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } // "[Ljava.lang.String;" style arrays if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) { String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); Class elementClass = forName(elementName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } // "[[I" or "[[Ljava.lang.String;" style arrays if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); Class elementClass = forName(elementName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } ClassLoader clToUse = classLoader; if (clToUse == null) { clToUse = getDefaultClassLoader(); } try { return Class.forName(name, false, clToUse); } catch (ClassNotFoundException ex) { int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR); if (lastDotIndex != -1) { String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1); try { return Class.forName(innerClassName, false, clToUse); } catch (ClassNotFoundException ex2) { // Swallow - let original exception get through } } throw ex; } } public static ClassLoader getDefaultClassLoader() { ClassLoader cl = null; try { cl = Thread.currentThread().getContextClassLoader(); } catch (Throwable ex) { // Cannot access thread context ClassLoader - falling back... } if (cl == null) { // No thread context class loader -> use class loader of this class. cl = Type.class.getClassLoader(); if (cl == null) { // getClassLoader() returning null indicates the bootstrap ClassLoader try { cl = ClassLoader.getSystemClassLoader(); } catch (Throwable ex) { // Cannot access system ClassLoader - oh well, maybe the caller can live with null... } } } return cl; } private static void registerCommonClasses(Class... commonClasses) { for (Class clazz : commonClasses) { COMMON_CLASS_CACHE.put(clazz.getName(), clazz); } } // ----------------------------------------------------------------------------------------------- // Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc. // ----------------------------------------------------------------------------------------------- /** * Returns the {@link Type} corresponding to the given type descriptor. * * @param typeDescriptor a field or method type descriptor. * @return the {@link Type} corresponding to the given type descriptor. */ public static Type getType(final String typeDescriptor) { return getTypeInternal(typeDescriptor, 0, typeDescriptor.length()); } /** * Returns the {@link Type} corresponding to the given class. * * @param clazz a class. * @return the {@link Type} corresponding to the given class. */ public static Type getType(final Class clazz) { if (clazz.isPrimitive()) { if (clazz == Integer.TYPE) { return INT_TYPE; } else if (clazz == Void.TYPE) { return VOID_TYPE; } else if (clazz == Boolean.TYPE) { return BOOLEAN_TYPE; } else if (clazz == Byte.TYPE) { return BYTE_TYPE; } else if (clazz == Character.TYPE) { return CHAR_TYPE; } else if (clazz == Short.TYPE) { return SHORT_TYPE; } else if (clazz == Double.TYPE) { return DOUBLE_TYPE; } else if (clazz == Float.TYPE) { return FLOAT_TYPE; } else if (clazz == Long.TYPE) { return LONG_TYPE; } else { throw new AssertionError(); } } else { return getType(getDescriptor(clazz)); } } /** * Returns the method {@link Type} corresponding to the given constructor. * * @param constructor a {@link Constructor} object. * @return the method {@link Type} corresponding to the given constructor. */ public static Type getType(final Constructor constructor) { return getType(getConstructorDescriptor(constructor)); } /** * Returns the method {@link Type} corresponding to the given method. * * @param method a {@link Method} object. * @return the method {@link Type} corresponding to the given method. */ public static Type getType(final Method method) { return getType(getMethodDescriptor(method)); } /** * Returns the {@link Type} corresponding to the given internal name. * * @param internalName an internal name. * @return the {@link Type} corresponding to the given internal name. */ public static Type getObjectType(final String internalName) { return new Type( internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length()); } /** * Returns the {@link Type} corresponding to the given method descriptor. Equivalent to * Type.getType(methodDescriptor). * * @param methodDescriptor a method descriptor. * @return the {@link Type} corresponding to the given method descriptor. */ public static Type getMethodType(final String methodDescriptor) { return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length()); } /** * Returns the method {@link Type} corresponding to the given argument and return types. * * @param returnType the return type of the method. * @param argumentTypes the argument types of the method. * @return the method {@link Type} corresponding to the given argument and return types. */ public static Type getMethodType(final Type returnType, final Type... argumentTypes) { return getType(getMethodDescriptor(returnType, argumentTypes)); } /** * Returns the {@link Type} values corresponding to the argument types of the given method * descriptor. * * @param methodDescriptor a method descriptor. * @return the {@link Type} values corresponding to the argument types of the given method * descriptor. */ public static Type[] getArgumentTypes(final String methodDescriptor) { // Second step: create a Type instance for each argument type. List argumentTypes = null; // Skip the first character, which is always a '('. int currentOffset = 1; while (methodDescriptor != null && methodDescriptor.charAt(currentOffset) != ')') { final int currentArgumentTypeOffset = currentOffset; while (methodDescriptor.charAt(currentOffset) == '[') { currentOffset++; } int genericCount = 0; if (methodDescriptor.charAt(currentOffset++) == 'L') { // Skip the argument descriptor content. while (true) { char c1 = methodDescriptor.charAt(currentOffset); if (c1 == '<') { int currentGenericCount = 0; int typeOffsetEnd = currentArgumentTypeOffset; List typeList = new ArrayList<>(); Type root = null; while (true) { char c = methodDescriptor.charAt(currentOffset); if (c == '<') { addGenericType(typeList, methodDescriptor, typeOffsetEnd, currentOffset); if (root == null) { root = typeList.get(0); } typeOffsetEnd = currentOffset + 1; currentGenericCount++; genericCount++; } else if (c == '>') { addGenericType(typeList, methodDescriptor, typeOffsetEnd, currentOffset - 1); typeList.remove(typeList.size() - 1); typeOffsetEnd = currentOffset; currentGenericCount--; } else if (currentGenericCount == 0) { break; } currentOffset++; } if (argumentTypes == null) { argumentTypes = new ArrayList<>(3); } argumentTypes.add(root); } else if (c1 == ';') { currentOffset++; break; } else { currentOffset++; } } } if (genericCount == 0) { if (argumentTypes == null) { argumentTypes = new ArrayList<>(3); } argumentTypes.add(getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset)); } } return argumentTypes == null ? EMPTY_TYPE : argumentTypes.toArray(new Type[argumentTypes.size()]); } private static void addGenericType(List typeList, String methodDescriptor, int begin, int end) { String descriptor = methodDescriptor.substring(begin, end); if (">".equals(descriptor)) { return; } descriptor = descriptor.concat(";"); Type type = getTypeInternal(descriptor, 0, descriptor.length()); if (!typeList.isEmpty()) { typeList.get(typeList.size() - 1).addGenericTypes(type); } typeList.add(type); } /** * Returns the {@link Type} values corresponding to the argument types of the given method. * * @param method a method. * @return the {@link Type} values corresponding to the argument types of the given method. */ public static Type[] getArgumentTypes(final Method method) { Class[] classes = method.getParameterTypes(); Type[] types = new Type[classes.length]; for (int i = classes.length - 1; i >= 0; --i) { types[i] = getType(classes[i]); } return types; } /** * Returns the {@link Type} corresponding to the return type of the given method descriptor. * * @param methodDescriptor a method descriptor. * @return the {@link Type} corresponding to the return type of the given method descriptor. */ public static Type getReturnType(final String methodDescriptor) { // Skip the first character, which is always a '('. int currentOffset = 1; // Skip the argument types, one at a each loop iteration. while (methodDescriptor.charAt(currentOffset) != ')') { while (methodDescriptor.charAt(currentOffset) == '[') { currentOffset++; } if (methodDescriptor.charAt(currentOffset++) == 'L') { // Skip the argument descriptor content. currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1; } } return getTypeInternal(methodDescriptor, currentOffset + 1, methodDescriptor.length()); } /** * Returns the {@link Type} corresponding to the return type of the given method. * * @param method a method. * @return the {@link Type} corresponding to the return type of the given method. */ public static Type getReturnType(final Method method) { return getType(method.getReturnType()); } /** * Returns the {@link Type} corresponding to the given field or method descriptor. * * @param descriptorBuffer a buffer containing the field or method descriptor. * @param descriptorBegin the beginning index, inclusive, of the field or method descriptor in * descriptorBuffer. * @param descriptorEnd the end index, exclusive, of the field or method descriptor in * descriptorBuffer. * @return the {@link Type} corresponding to the given type descriptor. */ private static Type getTypeInternal( final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) { switch (descriptorBuffer.charAt(descriptorBegin)) { case 'V': return VOID_TYPE; case 'Z': return BOOLEAN_TYPE; case 'C': return CHAR_TYPE; case 'B': return BYTE_TYPE; case 'S': return SHORT_TYPE; case 'I': return INT_TYPE; case 'F': return FLOAT_TYPE; case 'J': return LONG_TYPE; case 'D': return DOUBLE_TYPE; case '[': return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd); case 'L': return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1); case '*': return new Type(OBJECT_REF, descriptorBuffer, descriptorBegin + 2, descriptorEnd - 1); case '(': return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd); case 'T': return new Type(GENERIC_TYPE_NAME, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1); default: throw new IllegalArgumentException(); } } /** * Returns the internal name of the given class. The internal name of a class is its fully * qualified name, as returned by Class.getName(), where '.' are replaced by '/'. * * @param clazz an object or array class. * @return the internal name of the given class. */ public static String getInternalName(final Class clazz) { return clazz.getName().replace('.', '/'); } /** * Returns the descriptor corresponding to the given class. * * @param clazz an object class, a primitive class or an array class. * @return the descriptor corresponding to the given class. */ public static String getDescriptor(final Class clazz) { StringBuilder stringBuilder = new StringBuilder(); appendDescriptor(clazz, stringBuilder); return stringBuilder.toString(); } /** * Returns the descriptor corresponding to the given constructor. * * @param constructor a {@link Constructor} object. * @return the descriptor of the given constructor. */ public static String getConstructorDescriptor(final Constructor constructor) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('('); Class[] parameters = constructor.getParameterTypes(); for (Class parameter : parameters) { appendDescriptor(parameter, stringBuilder); } return stringBuilder.append(")V").toString(); } /** * Returns the descriptor corresponding to the given argument and return types. * * @param returnType the return type of the method. * @param argumentTypes the argument types of the method. * @return the descriptor corresponding to the given argument and return types. */ public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('('); for (Type argumentType : argumentTypes) { argumentType.appendDescriptor(stringBuilder); } stringBuilder.append(')'); returnType.appendDescriptor(stringBuilder); return stringBuilder.toString(); } // ----------------------------------------------------------------------------------------------- // Methods to get class names, internal names or descriptors. // ----------------------------------------------------------------------------------------------- /** * Returns the descriptor corresponding to the given method. * * @param method a {@link Method} object. * @return the descriptor of the given method. */ public static String getMethodDescriptor(final Method method) { return getMethodDescriptor(method.getParameterTypes(), method.getReturnType()); } public static String getMethodDescriptor(Class[] parameterTypes, Class returnType) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('('); for (Class parameter : parameterTypes) { appendDescriptor(parameter, stringBuilder); } stringBuilder.append(')'); appendDescriptor(returnType, stringBuilder); return stringBuilder.toString(); } /** * Appends the descriptor of the given class to the given string builder. * * @param clazz the class whose descriptor must be computed. * @param stringBuilder the string builder to which the descriptor must be appended. */ private static void appendDescriptor(final Class clazz, final StringBuilder stringBuilder) { Class currentClass = clazz; while (currentClass.isArray()) { stringBuilder.append('['); currentClass = currentClass.getComponentType(); } if (currentClass.isPrimitive()) { char descriptor; if (currentClass == Integer.TYPE) { descriptor = 'I'; } else if (currentClass == Void.TYPE) { descriptor = 'V'; } else if (currentClass == Boolean.TYPE) { descriptor = 'Z'; } else if (currentClass == Byte.TYPE) { descriptor = 'B'; } else if (currentClass == Character.TYPE) { descriptor = 'C'; } else if (currentClass == Short.TYPE) { descriptor = 'S'; } else if (currentClass == Double.TYPE) { descriptor = 'D'; } else if (currentClass == Float.TYPE) { descriptor = 'F'; } else if (currentClass == Long.TYPE) { descriptor = 'J'; } else { throw new AssertionError(); } stringBuilder.append(descriptor); } else { stringBuilder.append('L'); String name = currentClass.getName(); int nameLength = name.length(); for (int i = 0; i < nameLength; ++i) { char car = name.charAt(i); stringBuilder.append(car == '.' ? '/' : car); } stringBuilder.append(';'); } } /** * Computes the size of the arguments and of the return value of a method. * * @param methodDescriptor a method descriptor. * @return the size of the arguments of the method (plus one for the implicit this argument), * argumentsSize, and the size of its return value, returnSize, packed into a single int i = * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code * i >> 2}, and returnSize to {@code i & 0x03}). */ public static int getArgumentsAndReturnSizes(final String methodDescriptor) { int argumentsSize = 1; // Skip the first character, which is always a '('. int currentOffset = 1; int currentChar = methodDescriptor.charAt(currentOffset); // Parse the argument types and compute their size, one at a each loop iteration. while (currentChar != ')') { if (currentChar == 'J' || currentChar == 'D') { currentOffset++; argumentsSize += 2; } else { while (methodDescriptor.charAt(currentOffset) == '[') { currentOffset++; } if (methodDescriptor.charAt(currentOffset++) == 'L') { // Skip the argument descriptor content. currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1; } argumentsSize += 1; } currentChar = methodDescriptor.charAt(currentOffset); } currentChar = methodDescriptor.charAt(currentOffset + 1); if (currentChar == 'V') { return argumentsSize << 2; } else { int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1; return argumentsSize << 2 | returnSize; } } /** * Returns the type of the elements of this array type. This method should only be used for an * array type. * * @return Returns the type of the elements of this array type. */ public Type getElementType() { final int numDimensions = getDimensions(); return getTypeInternal(valueBuffer, valueBegin + numDimensions, valueEnd); } /** * Returns the argument types of methods of this type. This method should only be used for method * types. * * @return the argument types of methods of this type. */ public Type[] getArgumentTypes() { return getArgumentTypes(getDescriptor()); } /** * Returns the return type of methods of this type. This method should only be used for method * types. * * @return the return type of methods of this type. */ public Type getReturnType() { return getReturnType(getDescriptor()); } public Class resolveClass() { String className = getClassName(); try { return forName(className, null); } catch (IllegalAccessError err) { throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + err.getMessage(), err); } catch (LinkageError err) { throw new IllegalArgumentException("Unresolvable class definition for class [" + className + "]", err); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException("Could not find class [" + className + "]", ex); } } /** * Returns the binary name of the class corresponding to this type. This method must not be used * on method types. * * @return the binary name of the class corresponding to this type. */ public String getClassName() { switch (sort) { case VOID: return "void"; case BOOLEAN: return "boolean"; case CHAR: return "char"; case BYTE: return "byte"; case SHORT: return "short"; case INT: return "int"; case FLOAT: return "float"; case LONG: return "long"; case DOUBLE: return "double"; case ARRAY: StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName()); for (int i = getDimensions(); i > 0; --i) { stringBuilder.append("[]"); } return stringBuilder.toString(); case OBJECT: case INTERNAL: return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.'); default: throw new AssertionError(); } } /** * Returns the internal name of the class corresponding to this object or array type. The internal * name of a class is its fully qualified name (as returned by Class.getName(), where '.' are * replaced by '/'). This method should only be used for an object or array type. * * @return the internal name of the class corresponding to this object type. */ public String getInternalName() { return valueBuffer.substring(valueBegin, valueEnd); } /** * Returns the descriptor corresponding to this type. * * @return the descriptor corresponding to this type. */ public String getDescriptor() { if (sort == OBJECT) { return valueBuffer.substring(valueBegin - 1, valueEnd + 1); } else if (sort == INTERNAL) { return new StringBuilder() .append('L') .append(valueBuffer, valueBegin, valueEnd) .append(';') .toString(); } else { return valueBuffer.substring(valueBegin, valueEnd); } } // ----------------------------------------------------------------------------------------------- // Methods to get the sort, dimension, size, and opcodes corresponding to a Type or descriptor. // ----------------------------------------------------------------------------------------------- /** * Appends the descriptor corresponding to this type to the given string buffer. * * @param stringBuilder the string builder to which the descriptor must be appended. */ private void appendDescriptor(final StringBuilder stringBuilder) { if (sort == OBJECT) { stringBuilder.append(valueBuffer, valueBegin - 1, valueEnd + 1); } else if (sort == INTERNAL) { stringBuilder.append('L').append(valueBuffer, valueBegin, valueEnd).append(';'); } else { stringBuilder.append(valueBuffer, valueBegin, valueEnd); } } /** * Returns the sort of this type. * * @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link * #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or * {@link #METHOD}. */ public int getSort() { return sort == INTERNAL ? OBJECT : sort; } /** * Returns the number of dimensions of this array type. This method should only be used for an * array type. * * @return the number of dimensions of this array type. */ public int getDimensions() { int numDimensions = 1; while (valueBuffer.charAt(valueBegin + numDimensions) == '[') { numDimensions++; } return numDimensions; } /** * Returns the size of values of this type. This method must not be used for method types. * * @return the size of values of this type, i.e., 2 for {@code long} and {@code double}, 0 for * {@code void} and 1 otherwise. */ public int getSize() { switch (sort) { case VOID: return 0; case BOOLEAN: case CHAR: case BYTE: case SHORT: case INT: case FLOAT: case ARRAY: case OBJECT: case INTERNAL: return 1; case LONG: case DOUBLE: return 2; default: throw new AssertionError(); } } /** * Returns the size of the arguments and of the return value of methods of this type. This method * should only be used for method types. * * @return the size of the arguments of the method (plus one for the implicit this argument), * argumentsSize, and the size of its return value, returnSize, packed into a single int i = * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code * i >> 2}, and returnSize to {@code i & 0x03}). */ public int getArgumentsAndReturnSizes() { return getArgumentsAndReturnSizes(getDescriptor()); } /** * Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for * method types. * * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD, * IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and * IRETURN. * @return an opcode that is similar to the given opcode, but adapted to this {@link Type}. For * example, if this type is {@code float} and {@code opcode} is IRETURN, this method returns * FRETURN. */ public int getOpcode(final int opcode) { if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { switch (sort) { case BOOLEAN: case BYTE: return opcode + (Opcodes.BALOAD - Opcodes.IALOAD); case CHAR: return opcode + (Opcodes.CALOAD - Opcodes.IALOAD); case SHORT: return opcode + (Opcodes.SALOAD - Opcodes.IALOAD); case INT: return opcode; case FLOAT: return opcode + (Opcodes.FALOAD - Opcodes.IALOAD); case LONG: return opcode + (Opcodes.LALOAD - Opcodes.IALOAD); case DOUBLE: return opcode + (Opcodes.DALOAD - Opcodes.IALOAD); case ARRAY: case OBJECT: case INTERNAL: return opcode + (Opcodes.AALOAD - Opcodes.IALOAD); case METHOD: case VOID: throw new UnsupportedOperationException(); default: throw new AssertionError(); } } else { switch (sort) { case VOID: if (opcode != Opcodes.IRETURN) { throw new UnsupportedOperationException(); } return Opcodes.RETURN; case BOOLEAN: case BYTE: case CHAR: case SHORT: case INT: return opcode; case FLOAT: return opcode + (Opcodes.FRETURN - Opcodes.IRETURN); case LONG: return opcode + (Opcodes.LRETURN - Opcodes.IRETURN); case DOUBLE: return opcode + (Opcodes.DRETURN - Opcodes.IRETURN); case ARRAY: case OBJECT: case INTERNAL: if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) { throw new UnsupportedOperationException(); } return opcode + (Opcodes.ARETURN - Opcodes.IRETURN); case METHOD: throw new UnsupportedOperationException(); default: throw new AssertionError(); } } } // ----------------------------------------------------------------------------------------------- // Equals, hashCode and toString. // ----------------------------------------------------------------------------------------------- /** * Tests if the given object is equal to this type. * * @param object the object to be compared to this type. * @return {@literal true} if the given object is equal to this type. */ @Override public boolean equals(final Object object) { if (this == object) { return true; } if (!(object instanceof Type)) { return false; } Type other = (Type) object; if ((sort == INTERNAL ? OBJECT : sort) != (other.sort == INTERNAL ? OBJECT : other.sort)) { return false; } int begin = valueBegin; int end = valueEnd; int otherBegin = other.valueBegin; int otherEnd = other.valueEnd; // Compare the values. if (end - begin != otherEnd - otherBegin) { return false; } for (int i = begin, j = otherBegin; i < end; i++, j++) { if (valueBuffer.charAt(i) != other.valueBuffer.charAt(j)) { return false; } } return true; } /** * Returns a hash code value for this type. * * @return a hash code value for this type. */ @Override public int hashCode() { int hashCode = 13 * (sort == INTERNAL ? OBJECT : sort); if (sort >= ARRAY) { for (int i = valueBegin, end = valueEnd; i < end; i++) { hashCode = 17 * (hashCode + valueBuffer.charAt(i)); } } return hashCode; } /** * Returns a string representation of this type. * * @return the descriptor of this type. */ @Override public String toString() { return getDescriptor(); } } } public static class ClassReader implements Closeable { /** * 字节码数组 */ private byte[] codes; /** * 当前读取数组的下标 */ private int index; /** * 文件大小 */ private int length; /** * 标记下标,用于回滚 */ private int markIndex; public ClassReader(String path, String fileName) throws IOException { this(new FileInputStream(new File(path + File.separator + fileName))); } public ClassReader(Class clazz) throws IOException { this(clazz.getResourceAsStream(getClassFileName(clazz))); } public ClassReader(InputStream in) throws IOException { try { byte[] buffer = new byte[in.available()]; ByteArrayOutputStream out = new ByteArrayOutputStream(); while (in.read(buffer) != -1) { out.write(buffer); } this.codes = out.toByteArray(); this.length = this.codes.length; } finally { in.close(); } } public ClassReader(byte[] codes) { this.codes = codes; this.length = codes.length; } public void mark() { this.markIndex = index; } public void reset() { this.index = this.markIndex; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } /** * 读取8位-无符号 * * @return 8位-无符号 */ public short readUint8() { return (short) (readInt8() & 0x0FF); } /** * 读取8位-有符号 * * @return 8位-有符号 */ public byte readInt8() { byte value = codes[index]; index = index + 1; return value; } /** * 读取16位-无符号 * * @return 16位-无符号 */ public int readUint16() { return readInt16() & 0x0FFFF; } /** * 读取16位-有符号 * * @return 16位-有符号 */ public int readInt16() { int value = (short) (codes[index] << 8 | codes[index + 1] & 0xFF); index = index + 2; return value; } public int readUint32() { return readInt32() & 0x0FFFF; } /** * 读取32位-有符号 * * @return 32位-有符号 */ public int readInt32() { int value = (codes[index] & 0xff) << 24 | (codes[index + 1] & 0xff) << 16 | (codes[index + 2] & 0xff) << 8 | codes[index + 3] & 0xff; index = index + 4; return value; } /** * 读取64位-无符号 * * @return 64位-无符号 */ public long readUint64() { long value = ((long) codes[index] & 0xff) << 56 | ((long) codes[index + 1] & 0xff) << 48 | ((long) codes[index + 2] & 0xff) << 40 | ((long) codes[index + 3] & 0xff) << 32 | ((long) codes[index + 4] & 0xff) << 24 | ((long) codes[index + 5] & 0xff) << 16 | ((long) codes[index + 6] & 0xff) << 8 | (long) codes[index + 7] & 0xff; index = index + 8; return value; } /** * 读取16位-无符号-数组 * * @return 16位-无符号-数组 */ public int[] readUint16s() { int length = readUint16(); int[] values = new int[length]; for (int i = 0; i < length; i++) { values[i] = readUint16(); } return values; } /** * 读取8位-有符号-数组 * * @param length 长度 * @return 8位-有符号-数组 */ public byte[] readInt8s(int length) { byte[] values = new byte[length]; for (int i = 0; i < length; i++) { values[i] = readInt8(); } return values; } /** * 读取8位-无符号-数组 * * @param length 长度 * @return 8位-无符号-数组 */ public short[] readUInt8s(int length) { short[] values = new short[length]; for (int i = 0; i < length; i++) { values[i] = readUint8(); } return values; } public byte[] getBytes() { return codes; } public InputStream getInputStream() { return new ByteArrayInputStream(codes); } @Override public void close() { this.codes = null; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"file\":\"" + length + "b, " + (length / 1024) + "kb\"") .add("\"readIndex\":" + index) .toString(); } } public static class Opcodes { // Possible values for the type operand of the NEWARRAY instruction. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray. public static final int T_BOOLEAN = 4; public static final int T_CHAR = 5; public static final int T_FLOAT = 6; public static final int T_DOUBLE = 7; public static final int T_BYTE = 8; public static final int T_SHORT = 9; public static final int T_INT = 10; public static final int T_LONG = 11; // Possible values for the reference_kind field of CONSTANT_MethodHandle_info structures. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.8. public static final int H_GETFIELD = 1; public static final int H_GETSTATIC = 2; public static final int H_PUTFIELD = 3; public static final int H_PUTSTATIC = 4; public static final int H_INVOKEVIRTUAL = 5; public static final int H_INVOKESTATIC = 6; public static final int H_INVOKESPECIAL = 7; public static final int H_NEWINVOKESPECIAL = 8; public static final int H_INVOKEINTERFACE = 9; /** * Table 5.4.3.5-A. Bytecode Behaviors for Method Handles */ public static final String[] METHOD_HANDLES_NAMES = { null, "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic", "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial", "REF_newInvokeSpecial", "REF_invokeInterface" }; /** * An expanded frame. *

* A flag to expand the stack map frames. By default stack map frames are visited in their * original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed" * for the other classes). If this flag is set, stack map frames are always visited in expanded * format (this option adds a decompression/compression step in ClassReader and ClassWriter which * degrades performance quite a lot). */ public static final int F_NEW = -1; /** * A compressed frame with complete frame data. */ public static final int F_FULL = 0; /** * A compressed frame where locals are the same as the locals in the previous frame, except that * additional 1-3 locals are defined, and with an empty stack. */ public static final int F_APPEND = 1; /** * A compressed frame where locals are the same as the locals in the previous frame, except that * the last 1-3 locals are absent and with an empty stack. */ public static final int F_CHOP = 2; /** * A compressed frame with exactly the same locals as the previous frame and with an empty stack. */ public static final int F_SAME = 3; /** * A compressed frame with exactly the same locals as the previous frame and with a single value * on the stack. */ public static final int F_SAME1 = 4; // Standard stack map frame element types, . public static final byte ITEM_TOP = 0; public static final byte ITEM_INTEGER = 1; public static final byte ITEM_FLOAT = 2; public static final byte ITEM_DOUBLE = 3; public static final byte ITEM_LONG = 4; public static final byte ITEM_NULL = 5; public static final byte ITEM_UNINITIALIZED_THIS = 6; public static final byte ITEM_OBJECT = 7; public static final byte ITEM_UNINITIALIZED = 8; // The JVM opcode values (with the MethodVisitor method name used to visit them in comment, and // where '-' means 'same method name as on the previous line'). // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html. /** * Java VM opcodes. */ public static final short NOP = 0; public static final short ACONST_NULL = 1; public static final short ICONST_M1 = 2; public static final short ICONST_0 = 3; public static final short ICONST_1 = 4; public static final short ICONST_2 = 5; public static final short ICONST_3 = 6; public static final short ICONST_4 = 7; public static final short ICONST_5 = 8; public static final short LCONST_0 = 9; public static final short LCONST_1 = 10; public static final short FCONST_0 = 11; public static final short FCONST_1 = 12; public static final short FCONST_2 = 13; public static final short DCONST_0 = 14; public static final short DCONST_1 = 15; public static final short BIPUSH = 16; public static final short SIPUSH = 17; public static final short LDC = 18; public static final short LDC_W = 19; public static final short LDC2_W = 20; public static final short ILOAD = 21; public static final short LLOAD = 22; public static final short FLOAD = 23; public static final short DLOAD = 24; public static final short ALOAD = 25; public static final short ILOAD_0 = 26; public static final short ILOAD_1 = 27; public static final short ILOAD_2 = 28; public static final short ILOAD_3 = 29; public static final short LLOAD_0 = 30; public static final short LLOAD_1 = 31; public static final short LLOAD_2 = 32; public static final short LLOAD_3 = 33; public static final short FLOAD_0 = 34; public static final short FLOAD_1 = 35; public static final short FLOAD_2 = 36; public static final short FLOAD_3 = 37; public static final short DLOAD_0 = 38; public static final short DLOAD_1 = 39; public static final short DLOAD_2 = 40; public static final short DLOAD_3 = 41; public static final short ALOAD_0 = 42; public static final short ALOAD_1 = 43; public static final short ALOAD_2 = 44; public static final short ALOAD_3 = 45; public static final short IALOAD = 46; public static final short LALOAD = 47; public static final short FALOAD = 48; public static final short DALOAD = 49; public static final short AALOAD = 50; public static final short BALOAD = 51; public static final short CALOAD = 52; public static final short SALOAD = 53; public static final short ISTORE = 54; public static final short LSTORE = 55; public static final short FSTORE = 56; public static final short DSTORE = 57; public static final short ASTORE = 58; public static final short ISTORE_0 = 59; public static final short ISTORE_1 = 60; public static final short ISTORE_2 = 61; public static final short ISTORE_3 = 62; public static final short LSTORE_0 = 63; public static final short LSTORE_1 = 64; public static final short LSTORE_2 = 65; public static final short LSTORE_3 = 66; public static final short FSTORE_0 = 67; public static final short FSTORE_1 = 68; public static final short FSTORE_2 = 69; public static final short FSTORE_3 = 70; public static final short DSTORE_0 = 71; public static final short DSTORE_1 = 72; public static final short DSTORE_2 = 73; public static final short DSTORE_3 = 74; public static final short ASTORE_0 = 75; public static final short ASTORE_1 = 76; public static final short ASTORE_2 = 77; public static final short ASTORE_3 = 78; public static final short IASTORE = 79; public static final short LASTORE = 80; public static final short FASTORE = 81; public static final short DASTORE = 82; public static final short AASTORE = 83; public static final short BASTORE = 84; public static final short CASTORE = 85; public static final short SASTORE = 86; public static final short POP = 87; public static final short POP2 = 88; public static final short DUP = 89; public static final short DUP_X1 = 90; public static final short DUP_X2 = 91; public static final short DUP2 = 92; public static final short DUP2_X1 = 93; public static final short DUP2_X2 = 94; public static final short SWAP = 95; public static final short IADD = 96; public static final short LADD = 97; public static final short FADD = 98; public static final short DADD = 99; public static final short ISUB = 100; public static final short LSUB = 101; public static final short FSUB = 102; public static final short DSUB = 103; public static final short IMUL = 104; public static final short LMUL = 105; public static final short FMUL = 106; public static final short DMUL = 107; public static final short IDIV = 108; public static final short LDIV = 109; public static final short FDIV = 110; public static final short DDIV = 111; public static final short IREM = 112; public static final short LREM = 113; public static final short FREM = 114; public static final short DREM = 115; public static final short INEG = 116; public static final short LNEG = 117; public static final short FNEG = 118; public static final short DNEG = 119; public static final short ISHL = 120; public static final short LSHL = 121; public static final short ISHR = 122; public static final short LSHR = 123; public static final short IUSHR = 124; public static final short LUSHR = 125; public static final short IAND = 126; public static final short LAND = 127; public static final short IOR = 128; public static final short LOR = 129; public static final short IXOR = 130; public static final short LXOR = 131; public static final short IINC = 132; public static final short I2L = 133; public static final short I2F = 134; public static final short I2D = 135; public static final short L2I = 136; public static final short L2F = 137; public static final short L2D = 138; public static final short F2I = 139; public static final short F2L = 140; public static final short F2D = 141; public static final short D2I = 142; public static final short D2L = 143; public static final short D2F = 144; public static final short I2B = 145; public static final short INT2BYTE = 145; // Old notion public static final short I2C = 146; public static final short INT2CHAR = 146; // Old notion public static final short I2S = 147; public static final short INT2SHORT = 147; // Old notion public static final short LCMP = 148; public static final short FCMPL = 149; public static final short FCMPG = 150; public static final short DCMPL = 151; public static final short DCMPG = 152; public static final short IFEQ = 153; public static final short IFNE = 154; public static final short IFLT = 155; public static final short IFGE = 156; public static final short IFGT = 157; public static final short IFLE = 158; public static final short IF_ICMPEQ = 159; public static final short IF_ICMPNE = 160; public static final short IF_ICMPLT = 161; public static final short IF_ICMPGE = 162; public static final short IF_ICMPGT = 163; public static final short IF_ICMPLE = 164; public static final short IF_ACMPEQ = 165; public static final short IF_ACMPNE = 166; public static final short GOTO = 167; public static final short JSR = 168; public static final short RET = 169; public static final short TABLESWITCH = 170; public static final short LOOKUPSWITCH = 171; public static final short IRETURN = 172; public static final short LRETURN = 173; public static final short FRETURN = 174; public static final short DRETURN = 175; public static final short ARETURN = 176; public static final short RETURN = 177; public static final short GETSTATIC = 178; public static final short PUTSTATIC = 179; public static final short GETFIELD = 180; public static final short PUTFIELD = 181; public static final short INVOKEVIRTUAL = 182; public static final short INVOKESPECIAL = 183; public static final short INVOKENONVIRTUAL = 183; // Old name in JDK 1.0 public static final short INVOKESTATIC = 184; public static final short INVOKEINTERFACE = 185; public static final short NEW = 187; public static final short NEWARRAY = 188; public static final short ANEWARRAY = 189; public static final short ARRAYLENGTH = 190; public static final short ATHROW = 191; public static final short CHECKCAST = 192; public static final short INSTANCEOF = 193; public static final short MONITORENTER = 194; public static final short MONITOREXIT = 195; public static final short WIDE = 196; public static final short MULTIANEWARRAY = 197; public static final short IFNULL = 198; public static final short IFNONNULL = 199; public static final short GOTO_W = 200; public static final short JSR_W = 201; /** * Non-legal opcodes, may be used by JVM internally. */ public static final short BREAKPOINT = 202; public static final short LDC_QUICK = 203; public static final short LDC_W_QUICK = 204; public static final short LDC2_W_QUICK = 205; public static final short GETFIELD_QUICK = 206; public static final short PUTFIELD_QUICK = 207; public static final short GETFIELD2_QUICK = 208; public static final short PUTFIELD2_QUICK = 209; public static final short GETSTATIC_QUICK = 210; public static final short PUTSTATIC_QUICK = 211; public static final short GETSTATIC2_QUICK = 212; public static final short PUTSTATIC2_QUICK = 213; public static final short INVOKEVIRTUAL_QUICK = 214; public static final short INVOKENONVIRTUAL_QUICK = 215; public static final short INVOKESUPER_QUICK = 216; public static final short INVOKESTATIC_QUICK = 217; public static final short INVOKEINTERFACE_QUICK = 218; public static final short INVOKEVIRTUALOBJECT_QUICK = 219; public static final short NEW_QUICK = 221; public static final short ANEWARRAY_QUICK = 222; public static final short MULTIANEWARRAY_QUICK = 223; public static final short CHECKCAST_QUICK = 224; public static final short INSTANCEOF_QUICK = 225; public static final short INVOKEVIRTUAL_QUICK_W = 226; public static final short GETFIELD_QUICK_W = 227; public static final short PUTFIELD_QUICK_W = 228; public static final short IMPDEP1 = 254; public static final short IMPDEP2 = 255; public static final String ILLEGAL_OPCODE = ""; public static final String ILLEGAL_TYPE = ""; /** * Names of opcodes. */ public static final String[] OPCODE_NAMES = { "nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", "iconst_2", "iconst_3", "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0", "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush", "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload", "dload", "aload", "iload_0", "iload_1", "iload_2", "iload_3", "lload_0", "lload_1", "lload_2", "lload_3", "fload_0", "fload_1", "fload_2", "fload_3", "dload_0", "dload_1", "dload_2", "dload_3", "aload_0", "aload_1", "aload_2", "aload_3", "iaload", "laload", "faload", "daload", "aaload", "baload", "caload", "saload", "istore", "lstore", "fstore", "dstore", "astore", "istore_0", "istore_1", "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2", "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", "dstore_0", "dstore_1", "dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2", "astore_3", "iastore", "lastore", "fastore", "dastore", "aastore", "bastore", "castore", "sastore", "pop", "pop2", "dup", "dup_x1", "dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap", "iadd", "ladd", "fadd", "dadd", "isub", "lsub", "fsub", "dsub", "imul", "lmul", "fmul", "dmul", "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem", "frem", "drem", "ineg", "lneg", "fneg", "dneg", "ishl", "lshl", "ishr", "lshr", "iushr", "lushr", "iand", "land", "ior", "lor", "ixor", "lxor", "iinc", "i2l", "i2f", "i2d", "l2i", "l2f", "l2d", "f2i", "f2l", "f2d", "d2i", "d2l", "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl", "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt", "ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt", "if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", "goto", "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", "lreturn", "freturn", "dreturn", "areturn", "return", "getstatic", "putstatic", "getfield", "putfield", "invokevirtual", "invokespecial", "invokestatic", "invokeinterface", ILLEGAL_OPCODE, "new", "newarray", "anewarray", "arraylength", "athrow", "checkcast", "instanceof", "monitorenter", "monitorexit", "wide", "multianewarray", "ifnull", "ifnonnull", "goto_w", "jsr_w", "breakpoint", ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, ILLEGAL_OPCODE, "impdep1", "impdep2" }; private short[] opcodes; public Opcodes(short[] opcodes) { this.opcodes = opcodes; } public short[] getOpcodes() { return opcodes; } public String getOpcodeName(int pc) { return OPCODE_NAMES[opcodes[pc]]; } @Override public String toString() { StringJoiner joiner = new StringJoiner(",", "[", "]"); for (int i = 0; i < opcodes.length; i++) { int opcode = opcodes[i]; String name = OPCODE_NAMES[opcode]; joiner.add("{\"index\":" + i + ",\"opcode\":" + opcode + ",\"name\":\"" + name + "\"}"); } return joiner.toString(); } } public class Attribute extends LinkedHashMap { public Attribute(int attrNameIndex, int length, Attribute parent, ClassReader reader) { String attrName = constantPool.getUtf8(attrNameIndex); put("attrNameIndex", attrNameIndex); put("attrName", attrName); put("length", length); try { reader.mark(); decode(attrName, length, parent, reader); } catch (Exception e) { put("decodeAttributeException", e.toString()); reader.reset(); byte[] decodeAttributeExceptionBytes = reader.readInt8s(length); put("decodeAttributeExceptionBytes", decodeAttributeExceptionBytes); } } public Opcodes getOpcodes() { return (Opcodes) get("opcodes"); } private void decode(String attrName, int length, Attribute parent, ClassReader reader) { switch (attrName) { case "ConstantValue": { int constantValueIndex = reader.readUint16(); put("constantValueIndex", constantValueIndex); put("constantValue", constantPool.getConstantInfo(constantValueIndex)); break; } case "SourceFile": { int sourceFileIndex = reader.readUint16(); put("sourceFileIndex", sourceFileIndex); put("sourceFileName", constantPool.getUtf8(sourceFileIndex)); break; } case "Code": { put("maxStack", reader.readUint16()); put("maxLocals", reader.readUint16()); int codeLength = reader.readInt32(); short[] opcodes = reader.readUInt8s(codeLength); put("opcodes", new Opcodes(opcodes)); int codeExceptionsLength = reader.readUint16(); CodeException[] codeExceptions; if (codeExceptionsLength == 0) { codeExceptions = EMPTY_CODE_EXCEPTIONS; } else { codeExceptions = new CodeException[codeExceptionsLength]; for (int i = 0; i < codeExceptions.length; i++) { codeExceptions[i] = new CodeException(reader.readUint16(), reader.readUint16(), reader.readUint16(), reader.readUint16()); } } put("exceptionTable", codeExceptions); put("attributes", readAttributes(reader, this)); break; } case "Exceptions": { int exceptionIndexTableLength = reader.readUint16(); int[] exceptionIndexTable; String[] exceptionNameTable; if (exceptionIndexTableLength == 0) { exceptionIndexTable = EMPTY_EXCEPTION_INDEX_TABLE; exceptionNameTable = EMPTY_STRING; } else { exceptionIndexTable = new int[exceptionIndexTableLength]; for (int i = 0; i < exceptionIndexTable.length; i++) { exceptionIndexTable[i] = reader.readUint16(); } exceptionNameTable = new String[exceptionIndexTable.length]; for (int i = 0; i < exceptionIndexTable.length; i++) { exceptionNameTable[i] = constantPool.getClassNameForToString(exceptionIndexTable[i]); } } put("exceptionIndexTable", exceptionIndexTable); put("exceptionNameTable", exceptionNameTable); break; } case "LineNumberTable": { int lineNumberTableLength = reader.readUint16(); LineNumber[] lineNumberTable; if (lineNumberTableLength == 0) { lineNumberTable = EMPTY_LINE_NUMBER_TABLE; } else { Opcodes opcodes = parent.getOpcodes(); lineNumberTable = new LineNumber[lineNumberTableLength]; for (int i = 0; i < lineNumberTable.length; i++) { lineNumberTable[i] = new LineNumber(reader.readUint16(), reader.readUint16(), opcodes); } } put("lineNumberTable", lineNumberTable); break; } case "LocalVariableTable": case "LocalVariableTypeTable": { int localVariableTableLength = reader.readUint16(); LocalVariable[] localVariableTable; if (localVariableTableLength == 0) { localVariableTable = EMPTY_LOCAL_VARIABLE_TABLE; } else { localVariableTable = new LocalVariable[localVariableTableLength]; } for (int i = 0; i < localVariableTable.length; i++) { localVariableTable[i] = new LocalVariable( reader.readUint16(), reader.readUint16(), reader.readUint16(), reader.readUint16(), reader.readUint16()); } put("localVariableTable", localVariableTable); break; } case "InnerClasses": { int numberOfClassesLength = reader.readUint16(); InnerClass[] numberOfClasses; if (numberOfClassesLength == 0) { numberOfClasses = EMPTY_INNER_CLASSES; } else { numberOfClasses = new InnerClass[numberOfClassesLength]; } for (int i = 0; i < numberOfClasses.length; i++) { numberOfClasses[i] = new InnerClass( reader.readUint16(), reader.readUint16(), reader.readUint16(), reader.readUint16()); } put("numberOfClasses", numberOfClasses); break; } case "Synthetic": { if (length > 0) { byte[] syntheticBytes = reader.readInt8s(length); put("bytes", syntheticBytes); System.err.println("Synthetic attribute with length > 0"); } break; } case "Deprecated": { if (length > 0) { byte[] deprecatedBytes = reader.readInt8s(length); put("bytes", deprecatedBytes); System.err.println("Deprecated attribute with length > 0"); } break; } case "PMGClass": { put("pmgClassIndex", reader.readUint16()); put("pmgIndex", reader.readUint16()); break; } case "Signature": { int signatureIndex = reader.readUint16(); ConstantPool.ConstantUtf8Info info = (ConstantPool.ConstantUtf8Info) constantPool.getConstantInfo(signatureIndex); String signature = info.value(); StringBuilder typeBuilder = new StringBuilder(); StringBuilder genericBuilder = new StringBuilder(); // 复杂的暂时没实现. Ljava/util/Map;>; // Ljava/util/List; int count = 0; boolean generic = false; for (int i = 0; i < signature.length(); i++) { char c = signature.charAt(i); if (c == '<') { generic = true; } else if (c == '>') { generic = false; } else if (c == ';') { count++; genericBuilder.append(';'); } else if (generic) { genericBuilder.append(c); } else { typeBuilder.append(c); } } put("signatureIndex", signatureIndex); put("signature", info); if (count == 1) { put("signatureType", typeBuilder.toString()); put("signatureGeneric", genericBuilder.toString()); } break; } case "StackMap": { int stackMapsLength = reader.readUint16(); StackMapEntry[] stackMaps; if (stackMapsLength == 0) { stackMaps = EMPTY_STACK_MAP_ENTRY; } else { stackMaps = new StackMapEntry[stackMapsLength]; for (int i = 0; i < stackMaps.length; i++) { int byteCodeOffset = reader.readInt16(); int typesOfLocalsSize = reader.readUint16(); StackMapEntry stackMapEntry = new StackMapEntry(byteCodeOffset, typesOfLocalsSize); for (int j = 0; j < stackMapEntry.typesOfLocals.length; j++) { stackMapEntry.typesOfLocals[j] = new StackMapType(reader); } stackMaps[i] = stackMapEntry; } } put("map", stackMaps); break; } case "StackMapTable": { int numberOfEntries = reader.readUint16(); StackMapFrame[] entries; if (numberOfEntries == 0) { entries = EMPTY_STACK_MAP_FRAME; } else { entries = new StackMapFrame[numberOfEntries]; for (int i = 0; i < numberOfEntries; i++) { entries[i] = new StackMapFrame(reader); } } put("entries", entries); break; } case "RuntimeVisibleAnnotations": { int numberOfAnnotations = reader.readUint16(); Annotation[] annotations = new Annotation[numberOfAnnotations]; for (int i = 0; i < numberOfAnnotations; i++) { annotations[i] = new Annotation(reader, i); } put("annotations", annotations); break; } case "BootstrapMethods": { int numberOfBootstrapMethods = reader.readUint16(); BootstrapMethod[] bootstrapMethods; if (numberOfBootstrapMethods == 0) { bootstrapMethods = EMPTY_BOOT_STRAP_METHOD; } else { bootstrapMethods = new BootstrapMethod[numberOfBootstrapMethods]; } put("bootstrapMethods", bootstrapMethods); for (int i = 0; i < bootstrapMethods.length; i++) { bootstrapMethods[i] = new BootstrapMethod(reader); } break; } case "MethodParameters": { int parametersCount = reader.readUint8(); MethodParameter[] methodParameters; if (parametersCount == 0) { methodParameters = EMPTY_METHOD_PARAMETER; } else { methodParameters = new MethodParameter[parametersCount]; } put("methodParameters", methodParameters); for (int i = 0; i < methodParameters.length; i++) { methodParameters[i] = new MethodParameter(reader, i); } break; } default: { byte[] unkownBytes = reader.readInt8s(length); put("unkownBytes", unkownBytes); break; } } } public int length() { return (int) get("length"); } public String attrName() { return (String) get("attrName"); } public boolean isAttrLocalVariableTable() { return "LocalVariableTable".equals(attrName()); } public boolean isAttrLocalVariableTypeTable() { return "LocalVariableTypeTable".equals(attrName()); } public boolean isAttrCode() { return "Code".equals(attrName()); } public boolean isMethodParameters() { return "MethodParameters".equals(attrName()); } public boolean isRuntimeVisibleAnnotations() { return "RuntimeVisibleAnnotations".equals(attrName()); } public boolean isStackMapTable() { return "StackMapTable".equals(attrName()); } public boolean isLineNumberTable() { return "LineNumberTable".equals(attrName()); } public boolean isSignature() { return "Signature".equals(attrName()); } public LocalVariable[] localVariableTable() { Object localVariableTable = get("localVariableTable"); if (localVariableTable instanceof LocalVariable[]) { return (LocalVariable[]) localVariableTable; } return null; } public MethodParameter[] methodParameters() { Object methodParameters = get("methodParameters"); if (methodParameters instanceof MethodParameter[]) { return (MethodParameter[]) methodParameters; } return null; } public Attribute[] attributes() { Object attributes = get("attributes"); if (attributes instanceof Attribute[]) { return (Attribute[]) attributes; } return null; } @Override public String toString() { StringJoiner joiner = new StringJoiner(",", "{", "}"); Iterator> i = entrySet().iterator(); while (i.hasNext()) { Map.Entry e = i.next(); String key = e.getKey(); Object value = e.getValue(); if (value instanceof Number) { joiner.add("\"" + key + "\":" + value); } else if (value == null) { joiner.add("\"" + key + "\":null"); } else if (value.getClass().isArray()) { joiner.add("\"" + key + "\":" + toJsonArray(value)); } else if (value instanceof CharSequence) { joiner.add("\"" + key + "\":\"" + value + "\""); } else { joiner.add("\"" + key + "\":" + value); } } return joiner.toString(); } public class CodeException { private int startPc; private int endPc; private int handlerPc; private int catchType; public CodeException(int startPc, int endPc, int handlerPc, int catchType) { this.startPc = startPc; this.endPc = endPc; this.handlerPc = handlerPc; this.catchType = catchType; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"startPc\":" + startPc) .add("\"endPc\":" + endPc) .add("\"handlerPc\":" + handlerPc) .add("\"catchType\":" + catchType) .toString(); } } public class LineNumber { private int startPc; // Program Counter (PC) corresponds to line private int lineNumber; // number in source file private Opcodes opcodes; public LineNumber(int startPc, int lineNumber, Opcodes opcodes) { this.startPc = startPc; this.lineNumber = lineNumber; this.opcodes = opcodes; } public Opcodes getOpcodes() { return opcodes; } public int getLineNumber() { return lineNumber; } public int getStartPc() { return startPc; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"startPcName\":\"" + opcodes.getOpcodeName(startPc) + "\"") .add("\"startPc\":" + startPc) .add("\"lineNumber\":" + lineNumber) .toString(); } } public class LocalVariable { private int startPc; // Range in which the variable is valid private int length; private int nameIndex; // Index in constant pool of variable name private int signatureIndex; // Index of variable signature private int index; // Variable is `index'th local variable on this method's frame. public LocalVariable(int startPc, int length, int nameIndex, int signatureIndex, int index) { this.startPc = startPc; this.length = length; this.nameIndex = nameIndex; this.signatureIndex = signatureIndex; this.index = index; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"name\":\"" + constantPool.getUtf8ForToString(nameIndex) + "\"") .add("\"signatureName\":\"" + constantPool.getUtf8ForToString(signatureIndex) + "\"") .add("\"startPc\":" + startPc) .add("\"length\":" + length) .add("\"nameIndex\":" + nameIndex) .add("\"signatureIndex\":" + signatureIndex) .toString(); } public int startPc() { return startPc; } public int nameIndex() { return nameIndex; } public int signatureIndex() { return signatureIndex; } public int index() { return index; } public int length() { return length; } public String name() { return constantPool.getUtf8(nameIndex); } public String signatureName() { return constantPool.getUtf8(signatureIndex); } } public class InnerClass { private int innerClassIndex; private int outerClassIndex; private int innerNameIndex; private int innerAccessFlags; public InnerClass(int innerClassIndex, int outerClassIndex, int innerNameIndex, int innerAccessFlags) { this.innerClassIndex = innerClassIndex; this.outerClassIndex = outerClassIndex; this.innerNameIndex = innerNameIndex; this.innerAccessFlags = innerAccessFlags; } public String innerName() { if (innerNameIndex == 0) { return null; } else { return constantPool.getUtf8(innerNameIndex); } } public String innerClassName() { return constantPool.getClassName(innerClassIndex); } public String outerClassName() { if (outerClassIndex == 0) { return null; } else { return constantPool.getClassName(outerClassIndex); } } @Override public String toString() { String innerName = constantPool.getUtf8ForToString(innerNameIndex); String toStringInnerName = innerName == null ? "null" : "\"" + innerName + "\""; String outerClassName = constantPool.getClassNameForToString(outerClassIndex); String toStringOuterClassName = outerClassName == null ? "null" : "\"" + outerClassName + "\""; return new StringJoiner(",", "{", "}") .add("\"innerAccessFlags\":\"" + Modifier.toString(innerAccessFlags) + "\"") .add("\"innerName\":" + toStringInnerName) .add("\"innerClassName\":\"" + innerClassName() + "\"") .add("\"outerClassName\":" + toStringOuterClassName) .add("\"innerNameIndex\":" + innerNameIndex) .add("\"innerClassIndex\":" + innerClassIndex) .add("\"outerClassIndex\":" + outerClassIndex) .toString(); } } public class StackMapEntry { private int byteCodeOffset; private StackMapType[] typesOfLocals; public StackMapEntry(int byteCodeOffset, int typesOfLocalsSize) { this.byteCodeOffset = byteCodeOffset; if (typesOfLocalsSize == 0) { this.typesOfLocals = EMPTY_STACK_MAP_TYPE; } else { this.typesOfLocals = new StackMapType[typesOfLocalsSize]; } } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"byteCodeOffset\":" + byteCodeOffset) .add("\"typesOfLocals\":\"" + toJsonArray(typesOfLocals)) .toString(); } } public class StackMapType { private byte type; private int objectVariableIndex = -1; private int offset = -1; public StackMapType(ClassReader reader) { type = reader.readInt8(); if (type == Opcodes.ITEM_OBJECT) { objectVariableIndex = reader.readInt16(); } else if (type == Opcodes.ITEM_UNINITIALIZED) { offset = reader.readInt16(); } } public String getTypeName() { switch (type) { case Opcodes.ITEM_TOP: { return "top"; } case Opcodes.ITEM_INTEGER: { return "integer"; } case Opcodes.ITEM_FLOAT: { return "float"; } case Opcodes.ITEM_DOUBLE: { return "double"; } case Opcodes.ITEM_LONG: { return "long"; } case Opcodes.ITEM_NULL: { return "null"; } case Opcodes.ITEM_UNINITIALIZED_THIS: { return "uninitializedThis"; } case Opcodes.ITEM_OBJECT: { return "object"; } case Opcodes.ITEM_UNINITIALIZED: { return "uninitialized"; } default: { return "unkown"; } } } @Override public String toString() { StringJoiner joiner = new StringJoiner(",", "{", "}") .add("\"type\":" + type) .add("\"typeName\":\"" + getTypeName() + "\""); if (type == Opcodes.ITEM_OBJECT) { joiner.add("\"objectVariableIndex\":" + objectVariableIndex); joiner.add("\"objectVariable\":\"" + constantPool.getClassNameForToString(objectVariableIndex) + "\""); } if (type == Opcodes.ITEM_UNINITIALIZED) { joiner.add("\"offset\":" + offset); } return joiner.toString(); } } public class StackMapFrame { private short frameType; private String frameTypeName; private Integer offsetDelta; private StackMapType[] stacks; private StackMapType[] locals; public StackMapFrame(ClassReader reader) { frameType = reader.readUint8(); if (frameType >= 0 && frameType <= 63) { frameTypeName = "same"; } else if (frameType >= 64 && frameType <= 127) { frameTypeName = "same_locals_1_stack_item_frame"; stacks = new StackMapType[]{new StackMapType(reader)}; } else if (frameType == 247) { frameTypeName = "same_locals_1_stack_item_frame_extended"; offsetDelta = reader.readUint16(); stacks = new StackMapType[]{new StackMapType(reader)}; } else if (frameType >= 248 && frameType <= 250) { frameTypeName = "chop_frame"; offsetDelta = reader.readUint16(); } else if (frameType == 251) { frameTypeName = "same_frame_extended"; offsetDelta = reader.readUint16(); } else if (frameType >= 252 && frameType <= 254) { frameTypeName = "append_frame"; offsetDelta = reader.readUint16(); locals = new StackMapType[frameType - 251]; for (int i = 0; i < locals.length; i++) { locals[i] = new StackMapType(reader); } } else if (frameType == 255) { frameTypeName = "full_frame"; offsetDelta = reader.readUint16(); locals = new StackMapType[reader.readUint16()]; for (int i = 0; i < locals.length; i++) { locals[i] = new StackMapType(reader); } stacks = new StackMapType[reader.readUint16()]; for (int i = 0; i < stacks.length; i++) { stacks[i] = new StackMapType(reader); } } } public short getFrameType() { return frameType; } public String getFrameTypeName() { return frameTypeName; } public Integer getOffsetDelta() { return offsetDelta; } public StackMapType[] getStacks() { return stacks; } public StackMapType[] getLocals() { return locals; } @Override public String toString() { StringJoiner joiner = new StringJoiner(",", "{", "}") .add("\"frameType\":" + frameType) .add("\"frameTypeName\":\"" + frameTypeName + "\""); if (offsetDelta != null) { joiner.add("\"offsetDelta\":" + offsetDelta); } if (stacks != null) { joiner.add("\"stacks\":" + toJsonArray(stacks)); } if (locals != null) { joiner.add("\"locals\":" + toJsonArray(locals)); } return joiner.toString(); } } public class MethodParameter { public static final int FINAL = 0x00000010; public static final int SYNTHETIC = 0x00001000; public static final int MANDATED = 0x00008000; /** * 项目的值name_index必须为零或constant_pool 表中的有效索引。 * 如果该项的值name_index为零,则该parameters元素表示没有名称的形式参数。 * 如果项目的值name_index不为零,则constant_pool该索引处的 条目必须是一个 CONSTANT_Utf8_info结构 */ private int nameIndex; /** * 0x0010 ( ACC_FINAL) * 表示已声明形式参数 final。 *

* 0x1000 ( ACC_SYNTHETIC) * 表示根据编写源代码的语言规范(JLS §13.1),形参未在源代码中显式或隐式声明。(形参是生成此class文件的编译器的实现工件 。) *

* 0x8000 ( ACC_MANDATED) * 表示根据编写源代码的语言规范(JLS §13.1),在源代码中隐式声明了形参。 */ private int accessFlags; private int index; public MethodParameter(ClassReader reader, int index) { this.index = index; this.nameIndex = reader.readUint16(); this.accessFlags = reader.readUint16(); } public String getName() { String realName = getRealName(); if (realName == null || realName.isEmpty()) { return "arg" + index; } else { return realName; } } public String getRealName() { return constantPool.getUtf8ForToString(nameIndex); } public int getAccessFlags() { return accessFlags; } public int getIndex() { return index; } @Override public String toString() { StringJoiner joiner = new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"name\":\"" + constantPool.getUtf8ForToString(nameIndex) + "\"") .add("\"nameIndex\":" + nameIndex) .add("\"accessFlags\":" + accessFlags) .add("\"accFinal\":" + ((accessFlags & FINAL) != 0)) .add("\"accSynthetic\":" + ((accessFlags & SYNTHETIC) != 0)) .add("\"accMandated\":" + ((accessFlags & MANDATED) != 0)); return joiner.toString(); } } public class BootstrapMethod { private int bootstrapMethodRef; private int[] bootstrapArguments; public BootstrapMethod(ClassReader reader) { this.bootstrapMethodRef = reader.readUint16(); this.bootstrapArguments = new int[reader.readUint16()]; for (int i = 0; i < bootstrapArguments.length; i++) { this.bootstrapArguments[i] = reader.readUint16(); } } @Override public String toString() { ConstantPool.ConstantInfo[] infos = new ConstantPool.ConstantInfo[bootstrapArguments.length]; for (int i = 0; i < bootstrapArguments.length; i++) { int bootstrapArgument = bootstrapArguments[i]; infos[i] = constantPool.getConstantInfo(bootstrapArgument); } StringJoiner joiner = new StringJoiner(",", "{", "}") .add("\"bootstrapMethod\":" + constantPool.getConstantMethodHandleInfo(bootstrapMethodRef)) .add("\"bootstrapArguments\":" + Arrays.toString(infos)); return joiner.toString(); } } public class Annotation { private int index; private int typeIndex; private ElementValue[] elementValues; public Annotation(ClassReader reader, int index) { this.index = index; this.typeIndex = reader.readUint16(); this.elementValues = new ElementValue[reader.readUint16()]; for (int i = 0; i < elementValues.length; i++) { int valueIndex = reader.readUint16(); char tag = (char) reader.readInt8(); this.elementValues[i] = newElementValue(tag, reader, i); this.elementValues[i].valueIndex = valueIndex; } } public ElementValue newElementValue(char tag, ClassReader reader, int index) { ElementValue newElementValue; switch (tag) { case 'e': { EnumElementValue elementValue = new EnumElementValue(); elementValue.typeNameIndex = reader.readUint16(); elementValue.constNameIndex = reader.readUint16(); newElementValue = elementValue; break; } case '@': { AnnotationElementValue elementValue = new AnnotationElementValue(); elementValue.annotationValue = new Annotation(reader, index); newElementValue = elementValue; break; } case 'c': { ClassElementValue elementValue = new ClassElementValue(); elementValue.classInfoIndex = reader.readUint16(); newElementValue = elementValue; break; } case '[': { ArrayElementValue elementValue = new ArrayElementValue(); ElementValue[] elementValues = new ElementValue[reader.readUint16()]; for (int i = 0; i < elementValues.length; i++) { char elementTag = (char) reader.readInt8(); elementValues[i] = newElementValue(elementTag, reader, i); } elementValue.arrayValue = elementValues; newElementValue = elementValue; break; } case 'B': { ByteElementValue elementValue = new ByteElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 'C': { CharElementValue elementValue = new CharElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 'D': { DoubleElementValue elementValue = new DoubleElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 'F': { FloatElementValue elementValue = new FloatElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 'I': { IntElementValue elementValue = new IntElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 'J': { LongElementValue elementValue = new LongElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 'S': { ShortElementValue elementValue = new ShortElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 'Z': { BooleanElementValue elementValue = new BooleanElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } case 's': { StringElementValue elementValue = new StringElementValue(); elementValue.constValueIndex = reader.readUint16(); newElementValue = elementValue; break; } default: { throw new IllegalStateException("a unkown annotation tag. tag = '" + tag + "'"); } } newElementValue.tag = tag; return newElementValue; } @Override public String toString() { return new StringJoiner(",", "{", "}") .add("\"index\":" + index) .add("\"typeIndex\":" + typeIndex) .add("\"type\":\"" + constantPool.getUtf8ForToString(typeIndex) + "\"") .add("\"elementValues\":" + toJsonArray(elementValues)) .toString(); } public class ElementValue { protected int valueIndex; protected char tag; @Override public String toString() { StringJoiner joiner = new StringJoiner(",", "{", "}") .add("\"tag\":\"" + tag + "\"") .add("\"type\":\"" + getClass().getSimpleName() + "\""); if (valueIndex != 0) { joiner.add("\"valueIndex\":" + valueIndex); joiner.add("\"value\":\"" + constantPool.getUtf8ForToString(valueIndex) + "\""); } toStringAppend(joiner); return joiner.toString(); } public void toStringAppend(StringJoiner joiner) { } } public class ClassElementValue extends ElementValue { private int classInfoIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"classInfoIndex\":" + classInfoIndex) .add("\"classInfo\":\"" + constantPool.getUtf8ForToString(classInfoIndex) + "\""); } } public class StringElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":\"" + constantPool.getUtf8ForToString(constValueIndex) + "\""); } } public class ByteElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getInteger(constValueIndex)); } } public class CharElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getInteger(constValueIndex)); } } public class DoubleElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getDouble(constValueIndex)); } } public class FloatElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getFloat(constValueIndex)); } } public class IntElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getInteger(constValueIndex)); } } public class LongElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getLong(constValueIndex)); } } public class ShortElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getInteger(constValueIndex)); } } public class BooleanElementValue extends ElementValue { private int constValueIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constValueIndex\":" + constValueIndex) .add("\"constValue\":" + constantPool.getInteger(constValueIndex)); } } public class ArrayElementValue extends ElementValue { private ElementValue[] arrayValue; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"arrayValue\":" + toJsonArray(arrayValue)) .add("\"length\":" + arrayValue.length); } } public class AnnotationElementValue extends ElementValue { private Annotation annotationValue; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"annotationValue\":" + annotationValue); } } public class EnumElementValue extends ElementValue { private int constNameIndex; private int typeNameIndex; @Override public void toStringAppend(StringJoiner joiner) { joiner.add("\"constNameIndex\":" + constNameIndex) .add("\"constName\":\"" + constantPool.getUtf8ForToString(constNameIndex) + "\"") .add("\"typeNameIndex\":" + typeNameIndex) .add("\"typeName\":\"" + constantPool.getUtf8ForToString(typeNameIndex) + "\""); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy