package act.mail.bytecode; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * 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. * #L% */ import act.app.AppByteCodeScannerBase; import act.asm.*; import act.asm.signature.SignatureReader; import act.asm.signature.SignatureVisitor; import act.controller.meta.HandlerParamMetaInfo; import act.controller.meta.ParamAnnoInfoTrait; import act.mail.Mailer; import act.mail.meta.MailerClassMetaInfo; import act.mail.meta.MailerClassMetaInfoManager; import act.mail.meta.SenderMethodMetaInfo; import act.util.AsmTypes; import act.util.ByteCodeVisitor; import act.util.GeneralAnnoInfo; import org.osgl.$; import org.osgl.logging.L; import org.osgl.logging.Logger; import org.osgl.util.C; import java.util.List; import java.util.Map; /** * Mailer scanner implementation */ public class MailerByteCodeScanner extends AppByteCodeScannerBase { private final static Logger logger = L.get(MailerByteCodeScanner.class); private MailerClassMetaInfo classInfo; private volatile MailerClassMetaInfoManager classInfoBase; @Override protected boolean shouldScan(String className) { classInfo = new MailerClassMetaInfo(); return true; } @Override public ByteCodeVisitor byteCodeVisitor() { return new _ByteCodeVisitor(); } @Override public void scanFinished(String className) { classInfoBase().registerMailerMetaInfo(classInfo); } private MailerClassMetaInfoManager classInfoBase() { if (null == classInfoBase) { synchronized (this) { if (null == classInfoBase) { classInfoBase = app().classLoader().mailerClassMetaInfoManager(); } } } return classInfoBase; } public static boolean isMailerAnno(String desc) { return Type.getType(Mailer.class).getDescriptor().equals(desc); } private class _ByteCodeVisitor extends ByteCodeVisitor { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { logger.trace("Scanning %s", name); classInfo.className(name); if (isAbstract(access)) { classInfo.setAbstract(); } super.visit(version, access, name, signature, superName, interfaces); } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { if (classInfo.isMailer() && AsmTypes.ACTION_CONTEXT_DESC.equals(desc)) { classInfo.ctxField(name, isPrivate(access)); } return super.visitField(access, name, desc, signature, value); } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { AnnotationVisitor av = super.visitAnnotation(desc, visible); if (isMailerAnno(desc)) { classInfo.isMailer(true); return new MailerAnnotationVisitor(av); } return super.visitAnnotation(desc, visible); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (!classInfo.isMailer() || !isEligibleMethod(access, name, desc)) { return mv; } return new SenderMethodVisitor(mv, access, name, desc, signature, exceptions); } private boolean isEligibleMethod(int access, String name, String desc) { return isPublic(access) && !isAbstract(access) && !isConstructor(name) && name.startsWith("send"); } private class MailerAnnotationVisitor extends AnnotationVisitor { MailerAnnotationVisitor(AnnotationVisitor av) { super(ASM5, av); } @Override public void visit(String name, Object value) { if ("value".equals(name)) { classInfo.configId(value.toString()); } } } private class SenderMethodVisitor extends MethodVisitor implements Opcodes { private String methodName; private int access; private String desc; private String signature; private boolean requireScan; private SenderMethodMetaInfo methodInfo; private Map<Integer, List<ParamAnnoInfoTrait>> paramAnnoInfoList = C.newMap(); private Map<Integer, List<GeneralAnnoInfo>> genericParamAnnoInfoList = C.newMap(); SenderMethodVisitor(MethodVisitor mv, int access, String methodName, String desc, String signature, String[] exceptions) { super(ASM5, mv); this.access = access; this.methodName = methodName; this.desc = desc; this.signature = signature; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { AnnotationVisitor av = super.visitAnnotation(desc, visible); if (isMailerAnno(desc)) { if (null == methodInfo) { methodInfo = new SenderMethodMetaInfo(classInfo); classInfo.addSender(methodInfo); } return new SenderAnnotationVisitor(av); } return av; } @Override public void visitEnd() { if (null == methodInfo) { methodInfo = new SenderMethodMetaInfo(classInfo); classInfo.addSender(methodInfo); } final SenderMethodMetaInfo info = methodInfo; info.name(methodName); boolean isStatic = AsmTypes.isStatic(access); if (isStatic) { info.invokeStaticMethod(); } else { info.invokeInstanceMethod(); } info.returnType(Type.getReturnType(desc)); Type[] argTypes = Type.getArgumentTypes(desc); boolean ctxByParam = false; for (int i = 0; i < argTypes.length; ++i) { Type type = argTypes[i]; HandlerParamMetaInfo param = new HandlerParamMetaInfo().type(type); List<ParamAnnoInfoTrait> paraAnnoList = paramAnnoInfoList.get(i); if (null != paraAnnoList) { for (ParamAnnoInfoTrait trait : paraAnnoList) { trait.attachTo(param); } } List<GeneralAnnoInfo> list = genericParamAnnoInfoList.get(i); if (null != list) { param.addGeneralAnnotations(list); } info.addParam(param); } if (null != signature) { SignatureReader sr = new SignatureReader(signature); final $.Var<Integer> id = new $.Var<Integer>(-1); sr.accept(new SignatureVisitor(ASM5) { boolean startParsing; @Override public SignatureVisitor visitParameterType() { id.set(id.get() + 1); return this; } @Override public SignatureVisitor visitTypeArgument(char wildcard) { if (wildcard == '=') { startParsing = true; } return this; } @Override public void visitClassType(String name) { if (startParsing) { Type type = Type.getObjectType(name); int n = id.get(); if (n < 0) { info.returnComponentType(type); } else { info.param(n).componentType(type); } } startParsing = false; } }); } super.visitEnd(); } private class SenderAnnotationVisitor extends AnnotationVisitor implements Opcodes { public SenderAnnotationVisitor(AnnotationVisitor av) { super(ASM5, av); } @Override public void visit(String name, Object value) { if ("value".equals(name)) { methodInfo.configId(value.toString()); } } } } } }