diff --git a/src/main/java/org/redkale/asm/AsmMethodBean.java b/src/main/java/org/redkale/asm/AsmMethodBean.java new file mode 100644 index 000000000..e6521d4ec --- /dev/null +++ b/src/main/java/org/redkale/asm/AsmMethodBean.java @@ -0,0 +1,99 @@ +/* + * + */ +package org.redkale.asm; + +import java.util.ArrayList; +import java.util.List; +import org.redkale.convert.json.JsonConvert; + +/** + * 存放方法的字节信息 + * + * @since 2.8.0 + */ +public class AsmMethodBean { + + private List fieldNames; + + private int access; + + private String name; + + private String desc; + + private String signature; + + private String[] exceptions; + + public AsmMethodBean() { + } + + public AsmMethodBean(int access, String name, String desc, String signature, String[] exceptions) { + this.access = access; + this.name = name; + this.desc = desc; + this.signature = signature; + this.exceptions = exceptions; + this.fieldNames = new ArrayList<>(); + } + + void removeEmptyNames() { + if (fieldNames != null) { + while (fieldNames.remove(" ")); + } + } + + public List getFieldNames() { + return fieldNames; + } + + public void setFieldNames(List fieldNames) { + this.fieldNames = fieldNames; + } + + public int getAccess() { + return access; + } + + public void setAccess(int access) { + this.access = access; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String[] getExceptions() { + return exceptions; + } + + public void setExceptions(String[] exceptions) { + this.exceptions = exceptions; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/main/java/org/redkale/asm/AsmMethodBoost.java b/src/main/java/org/redkale/asm/AsmMethodBoost.java index ba4e455df..2456005f6 100644 --- a/src/main/java/org/redkale/asm/AsmMethodBoost.java +++ b/src/main/java/org/redkale/asm/AsmMethodBoost.java @@ -3,9 +3,14 @@ */ package org.redkale.asm; +import java.io.InputStream; import java.lang.reflect.Method; import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.redkale.annotation.Nullable; +import org.redkale.util.Utility; /** * 生产动态字节码的方法扩展器, 可以进行方法加强动作 @@ -24,6 +29,20 @@ public interface AsmMethodBoost { return new AsmMethodBoosts(items); } + /** + * 返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多 + * + * @param map + * @param clazz + * + * @return + */ + public static Map getMethodBean(Class clazz) { + Map rs = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), clazz); + rs.values().forEach(AsmMethodBean::removeEmptyNames); + return rs; + } + /** * 对方法进行动态加强处理 * @@ -101,4 +120,63 @@ public interface AsmMethodBoost { } } + + static class MethodParamClassVisitor extends ClassVisitor { + + private final Map fieldMap; + + public MethodParamClassVisitor(int api, final Map fieldmap) { + super(api); + this.fieldMap = fieldmap; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (java.lang.reflect.Modifier.isStatic(access)) { + return null; + } + String key = name + ":" + desc; + if (fieldMap.containsKey(key)) { + return null; + } + AsmMethodBean bean = new AsmMethodBean(access, name, desc, signature, exceptions); + List fieldNames = bean.getFieldNames(); + fieldMap.put(key, bean); + return new MethodVisitor(Opcodes.ASM6) { + @Override + public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { + if (index < 1) { + return; + } + int size = fieldNames.size(); + //index并不会按顺序执行的 + if (index > size) { + for (int i = size; i < index; i++) { + fieldNames.add(" "); + } + fieldNames.set(index - 1, name); + } + fieldNames.set(index - 1, name); + } + }; + } + + //返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多 + static Map getMethodParamNames(Map map, Class clazz) { + String n = clazz.getName(); + InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class"); + if (in == null) { + return map; + } + try { + new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM6, map), 0); + } catch (Exception e) { //无需理会 + } + Class superClass = clazz.getSuperclass(); + if (superClass == Object.class) { + return map; + } + return getMethodParamNames(map, superClass); + } + } } diff --git a/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java b/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java index 4e51c74eb..836ed5470 100644 --- a/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java +++ b/src/main/java/org/redkale/cache/spi/CacheAsmMethodBoost.java @@ -6,10 +6,15 @@ package org.redkale.cache.spi; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import org.redkale.asm.AnnotationVisitor; +import org.redkale.asm.AsmMethodBean; import org.redkale.asm.AsmMethodBoost; import org.redkale.asm.Asms; import org.redkale.asm.ClassWriter; +import org.redkale.asm.Label; import org.redkale.asm.MethodDebugVisitor; import static org.redkale.asm.Opcodes.ACC_PRIVATE; import static org.redkale.asm.Opcodes.ACC_PROTECTED; @@ -63,22 +68,35 @@ public class CacheAsmMethodBoost implements AsmMethodBoost { acc = ACC_PRIVATE; nowMethodName = newMethodName; } + final String rsMethodName = method.getName() + "_cache"; { + Map methodBeans = AsmMethodBoost.getMethodBean(serviceType); + String desc = Type.getMethodDescriptor(method); + AsmMethodBean methodBean = methodBeans.get(method.getName() + ":" + desc); + final String cacheDynDesc = Type.getDescriptor(CacheDyn.class); MethodDebugVisitor mv; AnnotationVisitor av; - String[] exps = null; - Class[] expTypes = method.getExceptionTypes(); - if (expTypes.length > 0) { - exps = new String[expTypes.length]; - for (int i = 0; i < expTypes.length; i++) { - exps[i] = expTypes[i].getName().replace('.', '/'); + String signature = null; + String[] exceptions = null; + if (methodBean == null) { + Class[] expTypes = method.getExceptionTypes(); + if (expTypes.length > 0) { + exceptions = new String[expTypes.length]; + for (int i = 0; i < expTypes.length; i++) { + exceptions[i] = expTypes[i].getName().replace('.', '/'); + } } + } else { + signature = methodBean.getSignature(); + exceptions = methodBean.getExceptions(); } //需要定义一个新方法调用 this.rsMethodName - mv = new MethodDebugVisitor(cw.visitMethod(acc, nowMethodName, Type.getMethodDescriptor(method), null, exps)); + mv = new MethodDebugVisitor(cw.visitMethod(acc, nowMethodName, Type.getMethodDescriptor(method), signature, exceptions)); //mv.setDebug(true); + Label l0 = new Label(); + mv.visitLabel(l0); { av = mv.visitAnnotation(cacheDynDesc, true); av.visitEnd(); @@ -100,6 +118,7 @@ public class CacheAsmMethodBoost implements AsmMethodBoost { mv.visitVarInsn(ALOAD, 0); //传参数 Class[] paramTypes = method.getParameterTypes(); + List insns = new ArrayList<>(); int insn = 0; for (Class pt : paramTypes) { insn++; @@ -116,6 +135,7 @@ public class CacheAsmMethodBoost implements AsmMethodBoost { } else { mv.visitVarInsn(ALOAD, insn); } + insns.add(insn); } mv.visitMethodInsn(INVOKESPECIAL, newDynName, rsMethodName, Type.getMethodDescriptor(method), false); if (method.getGenericReturnType() == void.class) { @@ -136,6 +156,15 @@ public class CacheAsmMethodBoost implements AsmMethodBoost { mv.visitInsn(ARETURN); } } + if (methodBean != null && paramTypes.length > 0) { + Label l2 = new Label(); + mv.visitLabel(l2); + //mv.visitLocalVariable("this", thisClassDesc, null, l0, l2, 0); + List fieldNames = methodBean.getFieldNames(); + for (int i = 0; i < paramTypes.length; i++) { + mv.visitLocalVariable(fieldNames.get(i), Type.getDescriptor(paramTypes[i]), null, l0, l2, insns.get(i)); + } + } mv.visitMaxs(20, 20); mv.visitEnd(); } diff --git a/src/main/java/org/redkale/net/sncp/Sncp.java b/src/main/java/org/redkale/net/sncp/Sncp.java index 00c94cfc0..5aee67542 100644 --- a/src/main/java/org/redkale/net/sncp/Sncp.java +++ b/src/main/java/org/redkale/net/sncp/Sncp.java @@ -560,6 +560,7 @@ public abstract class Sncp { Class clazz = serviceImplClass; Set methodKeys = new HashSet<>(); do { + Map methodBeans = AsmMethodBoost.getMethodBean(clazz); for (final Method method : clazz.getDeclaredMethods()) { String mk = Utility.methodKey(method); if (methodKeys.contains(mk)) { @@ -569,16 +570,27 @@ public abstract class Sncp { methodKeys.add(mk); String newMethodName = methodBoost.doMethod(cw, newDynName, FIELDPREFIX, method, null); if (newMethodName != null) { - String[] exps = null; - Class[] expTypes = method.getExceptionTypes(); - if (expTypes.length > 0) { - exps = new String[expTypes.length]; - for (int i = 0; i < expTypes.length; i++) { - exps[i] = expTypes[i].getName().replace('.', '/'); + String desc = Type.getMethodDescriptor(method); + AsmMethodBean methodBean = methodBeans.get(method.getName() + ":" + desc); + String signature = null; + String[] exceptions = null; + if (methodBean == null) { + Class[] expTypes = method.getExceptionTypes(); + if (expTypes.length > 0) { + exceptions = new String[expTypes.length]; + for (int i = 0; i < expTypes.length; i++) { + exceptions[i] = expTypes[i].getName().replace('.', '/'); + } } + } else { + signature = methodBean.getSignature(); + exceptions = methodBean.getExceptions(); } + //注意: 新方法会丢失原方法中的泛型信息signature //需要定义一个新方法调用 super.method - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PRIVATE, newMethodName, Type.getMethodDescriptor(method), null, exps)); + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PRIVATE, newMethodName, desc, signature, exceptions)); + Label l0 = new Label(); + mv.visitLabel(l0); //mv.setDebug(true); { //给参数加上原有的Annotation final Annotation[][] anns = method.getParameterAnnotations(); @@ -591,6 +603,7 @@ public abstract class Sncp { mv.visitVarInsn(ALOAD, 0); //传参数 Class[] paramTypes = method.getParameterTypes(); + List insns = new ArrayList<>(); int insn = 0; for (Class pt : paramTypes) { insn++; @@ -607,6 +620,7 @@ public abstract class Sncp { } else { mv.visitVarInsn(ALOAD, insn); } + insns.add(insn); } mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), Type.getMethodDescriptor(method), false); if (method.getGenericReturnType() == void.class) { @@ -627,6 +641,15 @@ public abstract class Sncp { mv.visitInsn(ARETURN); } } + if (methodBean != null && paramTypes.length > 0) { + Label l2 = new Label(); + mv.visitLabel(l2); + //mv.visitLocalVariable("this", thisClassDesc, null, l0, l2, 0); + List fieldNames = methodBean.getFieldNames(); + for (int i = 0; i < paramTypes.length; i++) { + mv.visitLocalVariable(fieldNames.get(i), Type.getDescriptor(paramTypes[i]), null, l0, l2, insns.get(i)); + } + } mv.visitMaxs(20, 20); mv.visitEnd(); }