/*
* Copyright 2008-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.hasor.core.classcode.aop;
import net.hasor.core.classcode.ASMEngineTools;
import net.hasor.core.classcode.asm.*;
import net.hasor.core.utils.ExceptionUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 该类的作用是在生成的类中加入aop的支持。
* @version 2010-9-2
* @author 赵永春 (zyc@hasor.net)
*/
@SuppressWarnings("deprecation")
class AopClassAdapter extends ClassVisitor implements Opcodes {
public final static String AopPrefix = "$aopFun"; //生成的Aop方法前缀
private String superClassName = null; //父类类名
private String thisClassName = null; //当前类名
private AopClassConfig classConfig = null; //Aop筛选器
private List<Method> aopMethodMap = null; //符合Aop的方法
private Set<String> validMethod = null; //代理类自身方法
//
public AopClassAdapter(final ClassVisitor visitor, AopClassConfig classConfig) {
super(ASM4, visitor);
this.classConfig = classConfig;
this.thisClassName = classConfig.getClassName().replace(".", "/");
this.aopMethodMap = new ArrayList<Method>(); //符合Aop的方法
this.validMethod = new HashSet<String>(); //代理类自身方法
}
//
/**asm.visit,用于保存类名。*/
public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
this.superClassName = name;
this.visitBegin();
super.visit(version, access, this.thisClassName, signature, this.superClassName, interfaces);
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
return null;
}
//
private void visitBegin() {
//1.采集符合Aop切面期望的方法。
Method[] methodSet1 = this.classConfig.getSuperClass().getMethods();
for (Method targetMethod : methodSet1) {
String tmDesc = ASMEngineTools.toAsmFullDesc(targetMethod);
int dataModifiers = targetMethod.getModifiers();
if (/**/ASMEngineTools.checkIn(dataModifiers, Modifier.PRIVATE) || //
ASMEngineTools.checkIn(dataModifiers, Modifier.FINAL) || //
ASMEngineTools.checkIn(dataModifiers, Modifier.STATIC)) {
continue;
}
//
AopInterceptor[] aop = this.classConfig.findInterceptor(tmDesc);
if (aop == null || aop.length == 0) {
continue;
}
this.aopMethodMap.add(targetMethod);
}
//2.输出Aop代理方法
for (Method targetMethod : this.aopMethodMap) {
int access = targetMethod.getModifiers();
String name = targetMethod.getName();
String desc = ASMEngineTools.toAsmDesc(targetMethod);
String signature = ASMEngineTools.toAsmSignature(targetMethod);
Class<?>[] errors = targetMethod.getExceptionTypes();
String[] exceptions = new String[errors.length];
for (int i = 0; i < errors.length; i++) {
exceptions[i] = ASMEngineTools.replaceClassName(errors[i]);
}
//
if (ASMEngineTools.checkIn(access, Modifier.NATIVE)) {
access = access - Modifier.NATIVE;
}
//
MethodVisitor mv = this.visitMethod(access, name, desc, signature, exceptions);
mv.visitCode();
this.visitProxyMethod(mv, name, desc);//输出新方法。
mv.visitEnd();
}
//3.输出“$aopFun”方法
for (Method targetMethod : this.aopMethodMap) {
int access = Modifier.PRIVATE;
String name = targetMethod.getName();
String desc = ASMEngineTools.toAsmDesc(targetMethod);
String signature = ASMEngineTools.toAsmSignature(targetMethod);
Class<?>[] errors = targetMethod.getExceptionTypes();
String[] exceptions = new String[errors.length];
for (int i = 0; i < errors.length; i++) {
exceptions[i] = ASMEngineTools.replaceClassName(errors[i]);
}
//
MethodVisitor mv = this.visitMethod(access, AopPrefix + name, desc, signature, exceptions);
mv.visitCode();
this.visitAOPMethod(mv, name, desc);//输出新方法。
mv.visitEnd();
}
//4.采集类本身的方法。
Method[] methodSet2 = this.classConfig.getSuperClass().getDeclaredMethods();
for (Method targetMethod : methodSet2) {
String selfMethodDesc = ASMEngineTools.toAsmFullDesc(targetMethod);
this.validMethod.add(selfMethodDesc);
}
}
/**处理构造方法*/
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
if ("<clinit>".equals(name)) {
return null;
}
//
//1.准备输出方法数据,该方法的主要目的是从desc中拆分出参数表和返回值。
Pattern p = Pattern.compile("\\((.*)\\)(.*)");
Matcher m = p.matcher(desc);
m.find();
String asmReturns = m.group(2);
asmReturns = asmReturns.charAt(0) == 'L' ? asmReturns.substring(1, asmReturns.length() - 1) : asmReturns;
//
//2.忽略构造方法,aop包装不会考虑构造方法。
if ("<init>".equals(name)) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
mv.visitCode();
this.visitConstruction(mv, name, desc);
mv.visitEnd();
return null;
}
//2.兼容如果有其它ClassVisitor动态输出方法时不会影响到它。
String testFullDesc = name + desc;
if (!this.validMethod.contains(testFullDesc)) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
return null;
}
//
//Code Builder “new Object[] { abc, abcc, abcc };”
private void codeBuilder_1(MethodVisitor mv, String[] asmParams) {
int paramCount = asmParams.length;
mv.visitIntInsn(Opcodes.BIPUSH, paramCount);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < paramCount; i++) {
String asmType = asmParams[i];
mv.visitInsn(Opcodes.DUP);
mv.visitIntInsn(Opcodes.BIPUSH, i);
if (asmParams[i].equals("B")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
} else if (asmParams[i].equals("S")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
} else if (asmParams[i].equals("I")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
} else if (asmParams[i].equals("J")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
} else if (asmParams[i].equals("F")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
} else if (asmParams[i].equals("D")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
} else if (asmParams[i].equals("C")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
} else if (asmParams[i].equals("Z")) {
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
} else {
mv.visitVarInsn(Opcodes.ALOAD, i + 1);
}
mv.visitInsn(Opcodes.AASTORE);
}
}
//Code Builder “new Class[] { int.class, Object.class, boolean.class, short.class };”
private void codeBuilder_2(MethodVisitor mv, String[] asmParams) {
int paramCount = asmParams.length;
mv.visitIntInsn(Opcodes.BIPUSH, paramCount);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class");
for (int i = 0; i < paramCount; i++) {
String asmType = asmParams[i];
mv.visitInsn(Opcodes.DUP);
mv.visitIntInsn(Opcodes.BIPUSH, i);
if (asmParams[i].equals("B")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;");
} else if (asmParams[i].equals("S")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;");
} else if (asmParams[i].equals("I")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
} else if (asmParams[i].equals("J")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
} else if (asmParams[i].equals("F")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
} else if (asmParams[i].equals("D")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;");
} else if (asmParams[i].equals("C")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;");
} else if (asmParams[i].equals("Z")) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
} else {
mv.visitLdcInsn(Type.getType(asmType));// Ljava/lang/Object;
}
mv.visitInsn(Opcodes.AASTORE);
}
}
//Code Builder “return ...”
private void codeBuilder_3(MethodVisitor mv, String asmReturns) {
if (asmReturns.equals("B")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Byte");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
mv.visitInsn(ASMEngineTools.getReturn("B"));
} else if (asmReturns.equals("S")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Short");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
mv.visitInsn(ASMEngineTools.getReturn("S"));
} else if (asmReturns.equals("I")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
mv.visitInsn(ASMEngineTools.getReturn("I"));
} else if (asmReturns.equals("J")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
mv.visitInsn(ASMEngineTools.getReturn("J"));
} else if (asmReturns.equals("F")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Float");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
mv.visitInsn(ASMEngineTools.getReturn("F"));
} else if (asmReturns.equals("D")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
mv.visitInsn(ASMEngineTools.getReturn("D"));
} else if (asmReturns.equals("C")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
mv.visitInsn(ASMEngineTools.getReturn("C"));
} else if (asmReturns.equals("Z")) {
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
mv.visitInsn(ASMEngineTools.getReturn("Z"));
} else if (asmReturns.equals("V")) {
mv.visitInsn(Opcodes.POP);
mv.visitInsn(Opcodes.RETURN);
} else {
mv.visitTypeInsn(Opcodes.CHECKCAST, ASMEngineTools.asmTypeToType(asmReturns));
mv.visitInsn(Opcodes.ARETURN);
}
}
//
private void visitProxyMethod(MethodVisitor mv, String name, String desc) {
// 生成的例子代码:
// public int doCall(int abc, Object abcc) {
// Class<?>[] pTypes = new Class[] { int.class, Object.class, boolean.class, short.class };
// Object[] pObjects = new Object[] { abc, abcc, abcc };
// try {
// Method m = this.getClass().getMethod("doCall", pTypes);
// InnerChainAopInvocation chain = new InnerChainAopInvocation(pObjects, m, this);
// Object obj = new InnerAopInvocation(m, chain).proceed();
// return ((Integer) obj).intValue();
// } catch (Throwable e) {
// throw new RuntimeException(e);
// }
// }
//1.准备输出方法数据
Pattern p = Pattern.compile("\\((.*)\\)(.*)");
Matcher m = p.matcher(desc);
m.find();
String[] asmParams = ASMEngineTools.splitAsmType(m.group(1));//"IIIILjava/lang/Integer;F[[[ILjava/lang.Boolean;"
String asmReturns = m.group(2);
int paramCount = asmParams.length;
int maxStack = 5;//方法最大堆栈大小
int maxLocals = paramCount + 5;//本地变量表大小
//
Label tryBegin = new Label();
Label tryEnd = new Label();
Label tryCatch = new Label();
Label lastReturn = new Label();
mv.visitTryCatchBlock(tryBegin, tryEnd, tryCatch, "java/lang/Throwable");
{//try {
mv.visitLabel(tryBegin);
this.codeBuilder_2(mv, asmParams);//Class<?>[] pTypes = new Class[] { int.class, Object.class, boolean.class, short.class };
mv.visitVarInsn(ASTORE, paramCount + 2);
this.codeBuilder_1(mv, asmParams);//Object[] pObjects = new Object[] { abc, abcc, abcc };
mv.visitVarInsn(ASTORE, paramCount + 3);
//
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, this.thisClassName, "getClass", "()Ljava/lang/Class;");
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, paramCount + 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
mv.visitVarInsn(ASTORE, paramCount + 4);
//
mv.visitTypeInsn(NEW, ASMEngineTools.replaceClassName(InnerChainAopInvocation.class));
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, paramCount + 4);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, paramCount + 3);
mv.visitMethodInsn(INVOKESPECIAL, ASMEngineTools.replaceClassName(InnerChainAopInvocation.class), "<init>", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)V");
mv.visitVarInsn(ASTORE, paramCount + 5);
//
mv.visitTypeInsn(NEW, ASMEngineTools.replaceClassName(InnerAopInvocation.class));
mv.visitInsn(DUP);
mv.visitLdcInsn(name + desc);
mv.visitVarInsn(ALOAD, paramCount + 4);
mv.visitVarInsn(ALOAD, paramCount + 5);
mv.visitMethodInsn(INVOKESPECIAL, ASMEngineTools.replaceClassName(InnerAopInvocation.class), "<init>", "(Ljava/lang/String;Ljava/lang/reflect/Method;" + ASMEngineTools.toAsmType(AopInvocation.class) + ")V");//
mv.visitMethodInsn(INVOKEVIRTUAL, ASMEngineTools.replaceClassName(InnerAopInvocation.class), "proceed", "()Ljava/lang/Object;");
mv.visitVarInsn(ASTORE, paramCount + 6);
mv.visitVarInsn(ALOAD, paramCount + 6);
mv.visitLabel(tryEnd);
this.codeBuilder_3(mv, asmReturns);
}
{//} catch (Exception e) {
mv.visitLabel(tryCatch);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });
mv.visitVarInsn(ASTORE, 5);
mv.visitVarInsn(ALOAD, 5);
mv.visitMethodInsn(INVOKESTATIC, ASMEngineTools.replaceClassName(ExceptionUtils.class), "toRuntimeException", "(Ljava/lang/Throwable;)Ljava/lang/RuntimeException;", false);
mv.visitInsn(ATHROW);
} // }
mv.visitMaxs(maxStack, maxLocals);
}
//
private void visitConstruction(MethodVisitor mv, String name, String desc) {
//1.准备输出方法数据
Pattern p = Pattern.compile("\\((.*)\\)(.*)");
Matcher m = p.matcher(desc);
m.find();
String[] asmParams = ASMEngineTools.splitAsmType(m.group(1));//"IIIILjava/lang/Integer;F[[[ILjava/lang.Boolean;"
int paramCount = asmParams.length;
//
mv.visitVarInsn(ALOAD, 0);
for (int i = 0; i < paramCount; i++) {
String asmType = asmParams[i];
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
}
mv.visitMethodInsn(INVOKESPECIAL, this.superClassName, name, desc);
mv.visitInsn(RETURN);
mv.visitMaxs(paramCount + 1, paramCount + 1);
}
//
private void visitAOPMethod(MethodVisitor mv, String name, String desc) {
//1.准备输出方法数据
Pattern p = Pattern.compile("\\((.*)\\)(.*)");
Matcher m = p.matcher(desc);
m.find();
String[] asmParams = ASMEngineTools.splitAsmType(m.group(1));//"IIIILjava/lang/Integer;F[[[ILjava/lang.Boolean;"
String asmReturns = m.group(2);
int paramCount = asmParams.length;
//
mv.visitVarInsn(ALOAD, 0);
for (int i = 0; i < paramCount; i++) {
String asmType = asmParams[i];
mv.visitVarInsn(ASMEngineTools.getLoad(asmType), i + 1);
}
mv.visitMethodInsn(INVOKESPECIAL, this.superClassName, name, desc);
mv.visitInsn(ASMEngineTools.getReturn(asmReturns));
mv.visitMaxs(paramCount + 1, paramCount + 1);
}
}