AsmMethodBean

This commit is contained in:
redkale
2023-12-20 15:27:19 +08:00
parent ef5349098d
commit 5d9bd3018a
4 changed files with 243 additions and 14 deletions

View File

@@ -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<String> 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<String> getFieldNames() {
return fieldNames;
}
public void setFieldNames(List<String> 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);
}
}

View File

@@ -3,9 +3,14 @@
*/ */
package org.redkale.asm; package org.redkale.asm;
import java.io.InputStream;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.redkale.annotation.Nullable; import org.redkale.annotation.Nullable;
import org.redkale.util.Utility;
/** /**
* 生产动态字节码的方法扩展器, 可以进行方法加强动作 * 生产动态字节码的方法扩展器, 可以进行方法加强动作
@@ -24,6 +29,20 @@ public interface AsmMethodBoost<T> {
return new AsmMethodBoosts(items); return new AsmMethodBoosts(items);
} }
/**
* 返回的List中参数列表可能会比方法参数量多因为方法内的临时变量也会存入list中 所以需要list的元素集合比方法的参数多
*
* @param map
* @param clazz
*
* @return
*/
public static Map<String, AsmMethodBean> getMethodBean(Class clazz) {
Map<String, AsmMethodBean> rs = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), clazz);
rs.values().forEach(AsmMethodBean::removeEmptyNames);
return rs;
}
/** /**
* 对方法进行动态加强处理 * 对方法进行动态加强处理
* *
@@ -101,4 +120,63 @@ public interface AsmMethodBoost<T> {
} }
} }
static class MethodParamClassVisitor extends ClassVisitor {
private final Map<String, AsmMethodBean> fieldMap;
public MethodParamClassVisitor(int api, final Map<String, AsmMethodBean> 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<String> 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<String, AsmMethodBean> getMethodParamNames(Map<String, AsmMethodBean> 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);
}
}
} }

View File

@@ -6,10 +6,15 @@ package org.redkale.cache.spi;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; 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.AnnotationVisitor;
import org.redkale.asm.AsmMethodBean;
import org.redkale.asm.AsmMethodBoost; import org.redkale.asm.AsmMethodBoost;
import org.redkale.asm.Asms; import org.redkale.asm.Asms;
import org.redkale.asm.ClassWriter; import org.redkale.asm.ClassWriter;
import org.redkale.asm.Label;
import org.redkale.asm.MethodDebugVisitor; import org.redkale.asm.MethodDebugVisitor;
import static org.redkale.asm.Opcodes.ACC_PRIVATE; import static org.redkale.asm.Opcodes.ACC_PRIVATE;
import static org.redkale.asm.Opcodes.ACC_PROTECTED; import static org.redkale.asm.Opcodes.ACC_PROTECTED;
@@ -63,22 +68,35 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
acc = ACC_PRIVATE; acc = ACC_PRIVATE;
nowMethodName = newMethodName; nowMethodName = newMethodName;
} }
final String rsMethodName = method.getName() + "_cache"; final String rsMethodName = method.getName() + "_cache";
{ {
Map<String, AsmMethodBean> methodBeans = AsmMethodBoost.getMethodBean(serviceType);
String desc = Type.getMethodDescriptor(method);
AsmMethodBean methodBean = methodBeans.get(method.getName() + ":" + desc);
final String cacheDynDesc = Type.getDescriptor(CacheDyn.class); final String cacheDynDesc = Type.getDescriptor(CacheDyn.class);
MethodDebugVisitor mv; MethodDebugVisitor mv;
AnnotationVisitor av; AnnotationVisitor av;
String[] exps = null; String signature = null;
Class<?>[] expTypes = method.getExceptionTypes(); String[] exceptions = null;
if (expTypes.length > 0) { if (methodBean == null) {
exps = new String[expTypes.length]; Class<?>[] expTypes = method.getExceptionTypes();
for (int i = 0; i < expTypes.length; i++) { if (expTypes.length > 0) {
exps[i] = expTypes[i].getName().replace('.', '/'); 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 //需要定义一个新方法调用 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); //mv.setDebug(true);
Label l0 = new Label();
mv.visitLabel(l0);
{ {
av = mv.visitAnnotation(cacheDynDesc, true); av = mv.visitAnnotation(cacheDynDesc, true);
av.visitEnd(); av.visitEnd();
@@ -100,6 +118,7 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
//传参数 //传参数
Class[] paramTypes = method.getParameterTypes(); Class[] paramTypes = method.getParameterTypes();
List<Integer> insns = new ArrayList<>();
int insn = 0; int insn = 0;
for (Class pt : paramTypes) { for (Class pt : paramTypes) {
insn++; insn++;
@@ -116,6 +135,7 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
} else { } else {
mv.visitVarInsn(ALOAD, insn); mv.visitVarInsn(ALOAD, insn);
} }
insns.add(insn);
} }
mv.visitMethodInsn(INVOKESPECIAL, newDynName, rsMethodName, Type.getMethodDescriptor(method), false); mv.visitMethodInsn(INVOKESPECIAL, newDynName, rsMethodName, Type.getMethodDescriptor(method), false);
if (method.getGenericReturnType() == void.class) { if (method.getGenericReturnType() == void.class) {
@@ -136,6 +156,15 @@ public class CacheAsmMethodBoost implements AsmMethodBoost {
mv.visitInsn(ARETURN); 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<String> 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.visitMaxs(20, 20);
mv.visitEnd(); mv.visitEnd();
} }

View File

@@ -560,6 +560,7 @@ public abstract class Sncp {
Class clazz = serviceImplClass; Class clazz = serviceImplClass;
Set<String> methodKeys = new HashSet<>(); Set<String> methodKeys = new HashSet<>();
do { do {
Map<String, AsmMethodBean> methodBeans = AsmMethodBoost.getMethodBean(clazz);
for (final Method method : clazz.getDeclaredMethods()) { for (final Method method : clazz.getDeclaredMethods()) {
String mk = Utility.methodKey(method); String mk = Utility.methodKey(method);
if (methodKeys.contains(mk)) { if (methodKeys.contains(mk)) {
@@ -569,16 +570,27 @@ public abstract class Sncp {
methodKeys.add(mk); methodKeys.add(mk);
String newMethodName = methodBoost.doMethod(cw, newDynName, FIELDPREFIX, method, null); String newMethodName = methodBoost.doMethod(cw, newDynName, FIELDPREFIX, method, null);
if (newMethodName != null) { if (newMethodName != null) {
String[] exps = null; String desc = Type.getMethodDescriptor(method);
Class<?>[] expTypes = method.getExceptionTypes(); AsmMethodBean methodBean = methodBeans.get(method.getName() + ":" + desc);
if (expTypes.length > 0) { String signature = null;
exps = new String[expTypes.length]; String[] exceptions = null;
for (int i = 0; i < expTypes.length; i++) { if (methodBean == null) {
exps[i] = expTypes[i].getName().replace('.', '/'); 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 //需要定义一个新方法调用 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); //mv.setDebug(true);
{ //给参数加上原有的Annotation { //给参数加上原有的Annotation
final Annotation[][] anns = method.getParameterAnnotations(); final Annotation[][] anns = method.getParameterAnnotations();
@@ -591,6 +603,7 @@ public abstract class Sncp {
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
//传参数 //传参数
Class[] paramTypes = method.getParameterTypes(); Class[] paramTypes = method.getParameterTypes();
List<Integer> insns = new ArrayList<>();
int insn = 0; int insn = 0;
for (Class pt : paramTypes) { for (Class pt : paramTypes) {
insn++; insn++;
@@ -607,6 +620,7 @@ public abstract class Sncp {
} else { } else {
mv.visitVarInsn(ALOAD, insn); mv.visitVarInsn(ALOAD, insn);
} }
insns.add(insn);
} }
mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), Type.getMethodDescriptor(method), false); mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), Type.getMethodDescriptor(method), false);
if (method.getGenericReturnType() == void.class) { if (method.getGenericReturnType() == void.class) {
@@ -627,6 +641,15 @@ public abstract class Sncp {
mv.visitInsn(ARETURN); 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<String> 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.visitMaxs(20, 20);
mv.visitEnd(); mv.visitEnd();
} }