package com.gmail.zahusek.tinyprotocolapi.asm.reflection; import com.gmail.zahusek.tinyprotocolapi.asm.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.*; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import static com.gmail.zahusek.tinyprotocolapi.asm.Opcodes.*; import static java.lang.reflect.Modifier.isStatic; class Accessor { private static final String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName(); private static final String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server"); public final Access access; private final ClassHolder holder; protected Accessor(ClassHolder holder, Access access) { this.holder = holder; this.access = access; } static <M> Map<Integer, M> dump(List<M> members) { HashMap<Integer, M> map = new HashMap<>(); int i = 0; for (M member : members) map.put(i++, member); return map; } public static Accessor get(String path) { path = StringUtils.replace(path, "{nms}", NMS_PREFIX); path = StringUtils.replace(path, "{obc}", OBC_PREFIX); Class<?> clazz = null; try { clazz = Class.forName(path); } catch (ClassNotFoundException e) { throw new RuntimeException("Class '" + path + "' is not found!"); } return get(clazz); } protected static Accessor get(Class<?> type) { ClassHolder holder = new ClassHolder(); ArrayList<Method> methods = new ArrayList<>(); ArrayList<Field> fields = new ArrayList<>(); ArrayList<Constructor<?>> constructors = new ArrayList<>(); collectMembers(type, methods, fields, constructors); int n = methods.size(); holder.methods = dump(methods); holder.methodModifiers = new int[n]; holder.parameterTypes = new Class[n][]; holder.methodTypes = new Class[n]; holder.methodNames = new String[n]; for (int i = 0; i < n; i++) { Method m = methods.get(i); holder.methodModifiers[i] = m.getModifiers(); holder.parameterTypes[i] = m.getParameterTypes(); holder.methodTypes[i] = m.getReturnType(); holder.methodNames[i] = m.getName(); } n = fields.size(); holder.fields = dump(fields); holder.enums = new HashMap<Integer, String>(); holder.fieldModifiers = new int[n]; holder.fieldTypes = new Class[n]; holder.fieldNames = new String[n]; for (int i = 0; i < n; i++) { Field f = fields.get(i); holder.fieldModifiers[i] = f.getModifiers(); holder.fieldTypes[i] = f.getType(); holder.fieldNames[i] = f.getName(); if(f.isEnumConstant()) holder.enums.put(i, f.getName()); } n = constructors.size(); holder.constructors = dump(constructors); holder.constructorModifiers = new int[n]; holder.constructorParameterTypes = new Class[n][]; for (int i = 0; i < n; i++) { Constructor<?> c = constructors.get(i); holder.constructorModifiers[i] = c.getModifiers(); holder.constructorParameterTypes[i] = c.getParameterTypes(); } holder.isNonStaticMemberClass = type.getEnclosingClass() != null && type.isMemberClass() && !isStatic(type.getModifiers()); holder.type = type; String className = type.getName(); String accessClassName = "ReflectASM.ClassAccess." + className; Class<?> accessClass; ClassManager loader = ClassManager.get(type); synchronized (loader) { try { accessClass = loader.loadClass(accessClassName); } catch (ClassNotFoundException ignored) { String accessClassNameInternal = accessClassName.replace('.', '/'); String classNameInternal = className.replace('.', '/'); final byte[] bytes = byteCode(holder, methods, fields, accessClassNameInternal, classNameInternal); try { accessClass = ClassUnsafe.theUnsafe.defineClass(accessClassName, bytes, 0, bytes.length, loader, type.getProtectionDomain()); } catch (Throwable ignored1){ accessClass = loader.defineClass(accessClassName, bytes); } } } try { Access access = (Access) accessClass.newInstance(); holder.access = access; return new Accessor(holder, access); } catch (Exception ex) { throw new RuntimeException("Error constructing method access class: " + accessClassName, ex); } } static <E extends Member> void addNonPrivate(List<E> list, E[] arr) { Collections.addAll(list, arr); } static void recursiveAddInterfaceMethodsToList(Class<?> interfaceType, List<Method> methods) { addNonPrivate(methods, interfaceType.getDeclaredMethods()); for (Class<?> nextInterface : interfaceType.getInterfaces()) recursiveAddInterfaceMethodsToList(nextInterface, methods); } static void collectMembers(Class<?> type, List<Method> methods, List<Field> fields, List<Constructor<?>> constructors) { if (type.isInterface()) { recursiveAddInterfaceMethodsToList(type, methods); return; } boolean search = true; for (Constructor<?> constructor : type.getDeclaredConstructors()) { int length = constructor.getParameterTypes().length; if (search) { switch (length) { case 0: constructors.add(0, constructor); search = false; break; case 1: constructors.add(0, constructor); break; default: constructors.add(constructor); break; } } } Class<?> nextClass = type; while (nextClass != Object.class) { addNonPrivate(fields, nextClass.getDeclaredFields()); addNonPrivate(methods, nextClass.getDeclaredMethods()); nextClass = nextClass.getSuperclass(); } } static byte[] byteCode(ClassHolder info, List<Method> methods, List<Field> fields, String accessClassNameInternal, String classNameInternal) { final String baseName = "sun/reflect/MagicAccessorImpl"; ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, baseName, new String[]{Access.class.getName().replace('.', '/')}); MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, baseName, "<init>", "()V", true); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); insertNewInstance(cw, classNameInternal, info); insertNewRawInstance(cw, classNameInternal); insertInvoke(cw, classNameInternal, methods); insertGetObject(cw, classNameInternal, fields); insertSetObject(cw, classNameInternal, fields); insertGetPrimitive(cw, classNameInternal, fields, Type.BOOLEAN_TYPE, "getBoolean", IRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.BOOLEAN_TYPE, "setBoolean", ILOAD); insertGetPrimitive(cw, classNameInternal, fields, Type.BYTE_TYPE, "getByte", IRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.BYTE_TYPE, "setByte", ILOAD); insertGetPrimitive(cw, classNameInternal, fields, Type.SHORT_TYPE, "getShort", IRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.SHORT_TYPE, "setShort", ILOAD); insertGetPrimitive(cw, classNameInternal, fields, Type.INT_TYPE, "getInt", IRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.INT_TYPE, "setInt", ILOAD); insertGetPrimitive(cw, classNameInternal, fields, Type.LONG_TYPE, "getLong", LRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.LONG_TYPE, "setLong", LLOAD); insertGetPrimitive(cw, classNameInternal, fields, Type.DOUBLE_TYPE, "getDouble", DRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.DOUBLE_TYPE, "setDouble", DLOAD); insertGetPrimitive(cw, classNameInternal, fields, Type.FLOAT_TYPE, "getFloat", FRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.FLOAT_TYPE, "setFloat", FLOAD); insertGetPrimitive(cw, classNameInternal, fields, Type.CHAR_TYPE, "getChar", IRETURN); insertSetPrimitive(cw, classNameInternal, fields, Type.CHAR_TYPE, "setChar", ILOAD); cw.visitEnd(); return cw.toByteArray(); } static void insertNewRawInstance(ClassWriter cw, String classNameInternal) { MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitTypeInsn(NEW, classNameInternal); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>", "()V", false); mv.visitInsn(ARETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } static void insertNewInstance(ClassWriter cw, String classNameInternal, ClassHolder info) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "newInstance", "(I[Ljava/lang/Object;)Ljava/lang/Object;", null, null); mv.visitCode(); int n = info.constructorModifiers.length; if (n != 0) { mv.visitVarInsn(ILOAD, 1); Label[] labels = new Label[n]; for (int i = 0; i < n; i++) labels[i] = new Label(); Label defaultLabel = new Label(); mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); StringBuilder buffer = new StringBuilder(128); for (int i = 0; i < n; i++) { mv.visitLabel(labels[i]); if (i == 0) mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{classNameInternal}, 0, null); else mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitTypeInsn(NEW, classNameInternal); mv.visitInsn(DUP); buffer.setLength(0); buffer.append('('); Class<?>[] paramTypes = info.constructorParameterTypes[i]; for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { mv.visitVarInsn(ALOAD, 2); mv.visitIntInsn(BIPUSH, paramIndex); mv.visitInsn(AALOAD); Type paramType = Type.getType(paramTypes[paramIndex]); unbox(mv, paramType); buffer.append(paramType.getDescriptor()); } buffer.append(")V"); mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>", buffer.toString(), true); mv.visitInsn(ARETURN); } mv.visitLabel(defaultLabel); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException"); mv.visitInsn(DUP); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("Constructor not found: "); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", true); mv.visitVarInsn(ILOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", true); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", true); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", true); mv.visitInsn(ATHROW); mv.visitMaxs(0, 0); mv.visitEnd(); } static void insertInvoke(ClassWriter cw, String classNameInternal, List<Method> methods) { MethodVisitor mv; mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "invoke", "(Ljava/lang/Object;I[Ljava/lang/Object;)Ljava/lang/Object;", null, null); mv.visitCode(); int n = methods.size(); if (n != 0) { mv.visitVarInsn(ILOAD, 2); Label[] labels = new Label[n]; for (int i = 0; i < n; i++) labels[i] = new Label(); Label defaultLabel = new Label(); mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); StringBuilder buffer = new StringBuilder(128); for (int i = 0; i < n; i++) { Method method = methods.get(i); boolean isInterface = method.getDeclaringClass().isInterface(); boolean isStatic = isStatic(method.getModifiers()); mv.visitLabel(labels[i]); if (i == 0) mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{classNameInternal}, 0, null); else mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); if (!isStatic) { mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, classNameInternal); } buffer.setLength(0); buffer.append('('); String methodName = method.getName(); Class<?>[] paramTypes = method.getParameterTypes(); Class<?> returnType = method.getReturnType(); for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { mv.visitVarInsn(ALOAD, 3); mv.visitIntInsn(BIPUSH, paramIndex); mv.visitInsn(AALOAD); Type paramType = Type.getType(paramTypes[paramIndex]); unbox(mv, paramType); buffer.append(paramType.getDescriptor()); } buffer.append(')'); buffer.append(Type.getDescriptor(returnType)); final int inv = isInterface ? INVOKEINTERFACE : (isStatic ? INVOKESTATIC : INVOKEVIRTUAL); mv.visitMethodInsn(inv, classNameInternal, methodName, buffer.toString(), true); final Type retType = Type.getType(returnType); box(mv, retType); mv.visitInsn(ARETURN); } mv.visitLabel(defaultLabel); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException"); mv.visitInsn(DUP); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("Method not found: "); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", true); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", true); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", true); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", true); mv.visitInsn(ATHROW); mv.visitMaxs(0, 0); mv.visitEnd(); } static void insertSetObject(ClassWriter cw, String classNameInternal, List<Field> fields) { int maxStack = 6; MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", null, null); mv.visitCode(); mv.visitVarInsn(ILOAD, 2); if (!fields.isEmpty()) { maxStack--; Label[] labels = new Label[fields.size()]; for (int i = 0, n = labels.length; i < n; i++) labels[i] = new Label(); Label defaultLabel = new Label(); mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); for (int i = 0, n = labels.length; i < n; i++) { Field field = fields.get(i); Type fieldType = Type.getType(field.getType()); boolean st = isStatic(field.getModifiers()); mv.visitLabel(labels[i]); mv.visitFrame(F_SAME, 0, null, 0, null); if (!st) { mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, classNameInternal); } mv.visitVarInsn(ALOAD, 3); unbox(mv, fieldType); mv.visitFieldInsn(st ? PUTSTATIC : PUTFIELD, classNameInternal, field.getName(), fieldType.getDescriptor()); mv.visitInsn(RETURN); } mv.visitLabel(defaultLabel); mv.visitFrame(F_SAME, 0, null, 0, null); } mv = insertThrowExceptionForFieldNotFound(mv); mv.visitMaxs(maxStack, 4); mv.visitEnd(); } static void insertGetObject(ClassWriter cw, String classNameInternal, List<Field> fields) { int maxStack = 6; MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitVarInsn(ILOAD, 2); if (!fields.isEmpty()) { maxStack--; Label[] labels = new Label[fields.size()]; for (int i = 0, n = labels.length; i < n; i++) labels[i] = new Label(); Label defaultLabel = new Label(); mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); for (int i = 0, n = labels.length; i < n; i++) { Field field = fields.get(i); mv.visitLabel(labels[i]); mv.visitFrame(F_SAME, 0, null, 0, null); if (isStatic(field.getModifiers())) { mv.visitFieldInsn(GETSTATIC, classNameInternal, field.getName(), Type.getDescriptor(field.getType())); } else { mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, classNameInternal); mv.visitFieldInsn(GETFIELD, classNameInternal, field.getName(), Type.getDescriptor(field.getType())); } Type fieldType = Type.getType(field.getType()); box(mv, fieldType); mv.visitInsn(ARETURN); } mv.visitLabel(defaultLabel); mv.visitFrame(F_SAME, 0, null, 0, null); } insertThrowExceptionForFieldNotFound(mv); mv.visitMaxs(maxStack, 3); mv.visitEnd(); } static void insertSetPrimitive(ClassWriter cw, String classNameInternal, List<Field> fields, Type primitiveType, String setterMethodName, int loadValueInstruction) { int maxStack = 6; int maxLocals = 5; final String typeNameInternal = primitiveType.getDescriptor(); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, setterMethodName, "(Ljava/lang/Object;I" + typeNameInternal + ")V", null, null); mv.visitCode(); mv.visitVarInsn(ILOAD, 2); if (!fields.isEmpty()) { maxStack--; Label[] labels = new Label[fields.size()]; Label labelForInvalidTypes = new Label(); boolean hasAnyBadTypeLabel = true; for (int i = 0, n = labels.length; i < n; i++) { if (Type.getType(fields.get(i).getType()).equals(primitiveType)){ labels[i] = new Label(); } else { labels[i] = labelForInvalidTypes; hasAnyBadTypeLabel = true; } } Label defaultLabel = new Label(); mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); for (int i = 0, n = labels.length; i < n; i++) { if (!labels[i].equals(labelForInvalidTypes)) { Field field = fields.get(i); mv.visitLabel(labels[i]); mv.visitFrame(F_SAME, 0, null, 0, null); if (isStatic(field.getModifiers())) { mv.visitVarInsn(loadValueInstruction, 3); mv.visitFieldInsn(PUTSTATIC, classNameInternal, field.getName(), typeNameInternal); } else { mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, classNameInternal); mv.visitVarInsn(loadValueInstruction, 3); mv.visitFieldInsn(PUTFIELD, classNameInternal, field.getName(), typeNameInternal); } mv.visitInsn(RETURN); } } if (hasAnyBadTypeLabel) { mv.visitLabel(labelForInvalidTypes); mv.visitFrame(F_SAME, 0, null, 0, null); insertThrowExceptionForFieldType(mv, primitiveType.getClassName()); } mv.visitLabel(defaultLabel); mv.visitFrame(F_SAME, 0, null, 0, null); } mv = insertThrowExceptionForFieldNotFound(mv); mv.visitMaxs(maxStack, maxLocals); mv.visitEnd(); } static void insertGetPrimitive(ClassWriter cw, String classNameInternal, List<Field> fields, Type primitiveType, String getterMethodName, int returnValueInstruction) { int maxStack = 6; final String typeNameInternal = primitiveType.getDescriptor(); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, getterMethodName, "(Ljava/lang/Object;I)" + typeNameInternal, null, null); mv.visitCode(); mv.visitVarInsn(ILOAD, 2); if (!fields.isEmpty()) { maxStack--; Label[] labels = new Label[fields.size()]; Label labelForInvalidTypes = new Label(); boolean hasAnyBadTypeLabel = false; for (int i = 0, n = labels.length; i < n; i++) { if (Type.getType(fields.get(i).getType()).equals(primitiveType)) labels[i] = new Label(); else { labels[i] = labelForInvalidTypes; hasAnyBadTypeLabel = true; } } Label defaultLabel = new Label(); mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels); for (int i = 0, n = labels.length; i < n; i++) { Field field = fields.get(i); if (!labels[i].equals(labelForInvalidTypes)) { mv.visitLabel(labels[i]); mv.visitFrame(F_SAME, 0, null, 0, null); if (isStatic(field.getModifiers())) { mv.visitFieldInsn(GETSTATIC, classNameInternal, field.getName(), typeNameInternal); } else { mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, classNameInternal); mv.visitFieldInsn(GETFIELD, classNameInternal, field.getName(), typeNameInternal); } mv.visitInsn(returnValueInstruction); } } if (hasAnyBadTypeLabel) { mv.visitLabel(labelForInvalidTypes); mv.visitFrame(F_SAME, 0, null, 0, null); insertThrowExceptionForFieldType(mv, primitiveType.getClassName()); } mv.visitLabel(defaultLabel); mv.visitFrame(F_SAME, 0, null, 0, null); } mv = insertThrowExceptionForFieldNotFound(mv); mv.visitMaxs(maxStack, 3); mv.visitEnd(); } static MethodVisitor insertThrowExceptionForFieldNotFound(MethodVisitor mv) { mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException"); mv.visitInsn(DUP); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("Field not found: "); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", true); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", true); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", true); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", true); mv.visitInsn(ATHROW); return mv; } static MethodVisitor insertThrowExceptionForFieldType(MethodVisitor mv, String fieldType) { mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException"); mv.visitInsn(DUP); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("Field not declared as " + fieldType + ": "); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", true); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", true); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", true); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", true); mv.visitInsn(ATHROW); return mv; } static void box(MethodVisitor mv, Type type) { switch (type.getSort()) { case Type.VOID: mv.visitInsn(ACONST_NULL); break; case Type.BOOLEAN: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", true); break; case Type.BYTE: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", true); break; case Type.CHAR: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", true); break; case Type.SHORT: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", true); break; case Type.INT: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", true); break; case Type.FLOAT: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", true); break; case Type.LONG: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", true); break; case Type.DOUBLE: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", true); break; } } static void unbox(MethodVisitor mv, Type type) { switch (type.getSort()) { case Type.BOOLEAN: mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", true); break; case Type.BYTE: mv.visitTypeInsn(CHECKCAST, "java/lang/Number"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "byteValue", "()B", true); break; case Type.CHAR: mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", true); break; case Type.SHORT: mv.visitTypeInsn(CHECKCAST, "java/lang/Number"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "shortValue", "()S", true); break; case Type.INT: mv.visitTypeInsn(CHECKCAST, "java/lang/Number"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "intValue", "()I", true); break; case Type.FLOAT: mv.visitTypeInsn(CHECKCAST, "java/lang/Number"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "floatValue", "()F", true); break; case Type.LONG: mv.visitTypeInsn(CHECKCAST, "java/lang/Number"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "longValue", "()J", true); break; case Type.DOUBLE: mv.visitTypeInsn(CHECKCAST, "java/lang/Number"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "doubleValue", "()D", true); break; case Type.ARRAY: mv.visitTypeInsn(CHECKCAST, type.getDescriptor()); break; case Type.OBJECT: mv.visitTypeInsn(CHECKCAST, type.getInternalName()); break; } } @Override public String toString() { return access.toString(); } public boolean isNonStaticMemberClass() { return holder.isNonStaticMemberClass; } public int[] getConstructorModifiers() { return holder.constructorModifiers; } public Class<?>[][] getConstructorParameterTypes() { return holder.constructorParameterTypes; } public int[] getMethodModifiers() { return holder.methodModifiers; } public int[] getFieldModifiers() { return holder.fieldModifiers; } public int indexOfMethod(String methodName) { for (int i = 0, n = holder.methodNames.length; i < n; i++) if (holder.methodNames[i].equals(methodName)) return i; throw new IllegalArgumentException("Unable to find public method: " + methodName); } public int indexOfMethod(String methodName, Class<?>... paramTypes) { final String[] methodNames = holder.methodNames; for (int i = 0, n = methodNames.length; i < n; i++) if (methodNames[i].equals(methodName) && Arrays.equals(paramTypes, holder.parameterTypes[i])) return i; throw new IllegalArgumentException("Unable to find public method: " + methodName + " " + Arrays.toString(paramTypes)); } public int indexOfMethod(String methodName, int paramsCount) { final String[] methodNames = holder.methodNames; final Class<?>[][] parameterTypes = holder.parameterTypes; for (int i = 0, n = methodNames.length; i < n; i++) { if (methodNames[i].equals(methodName) && parameterTypes[i].length == paramsCount) return i; } throw new IllegalArgumentException("Unable to find public method: " + methodName + " with " + paramsCount + " params."); } public int indexOfMethod(Class<?> type, Class<?>... paramTypes) { for (int i = 0, n = holder.methodNames.length; i < n; i++) { if (holder.methodTypes[i].equals(type) && Arrays.equals(paramTypes, holder.parameterTypes[i])) return i; } throw new IllegalArgumentException("Unable to find public method type: " + type + " with " + Arrays.toString(paramTypes) + " params."); } public String[] getMethodNames() { return holder.methodNames; } public Class<?>[][] getParameterTypes() { return holder.parameterTypes; } public Class<?>[] getMethodTypes() { return holder.methodTypes; } public String[] getFieldNames() { return holder.fieldNames; } public Class<?>[] getFieldTypes() { return holder.fieldTypes; } public int getFieldCount() { return holder.fieldTypes.length; } public int indexOfField(String fieldName) { String[] fieldNames = holder.fieldNames; for (int i = 0, n = fieldNames.length; i < n; i++) if (fieldNames[i].equals(fieldName)) return i; throw new IllegalArgumentException("Unable to find public field: " + fieldName); } public int indexOfField(Class<?> fieldReturn, int index) { Class<?>[] fieldNames = holder.fieldTypes; for (int i = index, n = fieldNames.length; i < n; ++i) if (fieldNames[i].isAssignableFrom(fieldReturn)) return i; throw new IllegalArgumentException("Unable to find public field: " + fieldReturn); } public Object newInstance(int constructorIndex, Object... args) { return access.newInstance(constructorIndex, args); } public Object newInstance() { return access.newInstance(); } public Object invoke(Object instance, int methodIndex, Object... args) { return access.invoke(instance, methodIndex, args); } public void set(Object instance, int fieldIndex, Object value) { access.set(instance, fieldIndex, value); } public void setBoolean(Object instance, int fieldIndex, boolean value) { access.setBoolean(instance, fieldIndex, value); } public void setByte(Object instance, int fieldIndex, byte value) { access.setByte(instance, fieldIndex, value); } public void setShort(Object instance, int fieldIndex, short value) { access.setShort(instance, fieldIndex, value); } public void setInt(Object instance, int fieldIndex, int value) { access.setInt(instance, fieldIndex, value); } public void setLong(Object instance, int fieldIndex, long value) { access.setLong(instance, fieldIndex, value); } public void setDouble(Object instance, int fieldIndex, double value) { access.setDouble(instance, fieldIndex, value); } public void setFloat(Object instance, int fieldIndex, float value) { access.setFloat(instance, fieldIndex, value); } public void setChar(Object instance, int fieldIndex, char value) { access.setChar(instance, fieldIndex, value); } public Object get(Object instance, int fieldIndex) { return access.get(instance, fieldIndex); } public char getChar(Object instance, int fieldIndex) { return access.getChar(instance, fieldIndex); } public boolean getBoolean(Object instance, int fieldIndex) { return access.getBoolean(instance, fieldIndex); } public byte getByte(Object instance, int fieldIndex) { return access.getByte(instance, fieldIndex); } public short getShort(Object instance, int fieldIndex) { return access.getShort(instance, fieldIndex); } public int getInt(Object instance, int fieldIndex) { return access.getInt(instance, fieldIndex); } public long getLong(Object instance, int fieldIndex) { return access.getLong(instance, fieldIndex); } public double getDouble(Object instance, int fieldIndex) { return access.getDouble(instance, fieldIndex); } public float getFloat(Object instance, int fieldIndex) { return access.getFloat(instance, fieldIndex); } public int indexOfEnum(String enumName) { String[] enumNames = holder.fieldNames; for (int i = 0, n = enumNames.length; i < n; i++) if (enumNames[i].equals(enumName)) return i; throw new IllegalArgumentException("Unable to find public field: " + enumName); } public String[] getEnumNames() { return (String[]) holder.enums.values().toArray(); } public int getEnumCount() { return holder.enums.size(); } public Class<?> getCanonicalClass(String path){ path = StringUtils.replace(path, "{nms}", NMS_PREFIX); path = StringUtils.replace(path, "{obc}", OBC_PREFIX); Class<?> clazz = null; try { clazz = Class.forName(path); } catch (ClassNotFoundException e) { throw new RuntimeException("Class '" + path + "' is not found!"); } return clazz; } public Class<?> getClassType(){ return holder.type; } }