package act.util; /*- * #%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.AnnotationVisitor; import act.asm.MethodVisitor; import act.asm.Type; import static act.util.ClassFinderData.By.ANNOTATION; import static act.util.ClassFinderData.By.SUPER_TYPE; /** * Scans all public non-abstract methods for {@link SubClassFinder} * annotations. If found then it will create a {@link ClassFinderData} * and schedule it to run finding process */ public class ClassFinderByteCodeScanner extends AppByteCodeScannerBase { @Override protected boolean shouldScan(String className) { return true; } @Override public ByteCodeVisitor byteCodeVisitor() { return new _ByteCodeVisitor(); } @Override public void scanFinished(String className) { } private class _ByteCodeVisitor extends ByteCodeVisitor { private String className; @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); if (!AsmTypes.isPublic(access)) { return; } className = Type.getObjectType(name).getClassName(); } @Override public MethodVisitor visitMethod(int access, String name, String desc, final String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (null == className || !AsmTypes.isPublicNotAbstract(access)) { return mv; } final String methodName = name; final boolean isStatic = AsmTypes.isStatic(access); return new MethodVisitor(ASM5, mv) { @Override public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { Type annoType = Type.getType(desc); ClassFinderData.By by = null; if (AsmTypes.SUB_CLASS_FINDER.asmType().equals(annoType)) { by = SUPER_TYPE; } else if (AsmTypes.ANN_CLASS_FINDER.asmType().equals(annoType)) { by = ANNOTATION; } AnnotationVisitor av = super.visitAnnotation(desc, visible); if (null == by) { return av; } final ClassFinderData.By how = by; return new AnnotationVisitor(ASM5, av) { ClassFinderData finder = new ClassFinderData(); @Override public void visit(String name, Object value) { if ("value".equals(name)) { Type type = (Type) value; String className = type.getClassName(); finder.what(className); } else if ("publicOnly".equals(name)) { finder.publicOnly((Boolean) value); } else if ("noAbstract".equals(name)) { finder.noAbstract((Boolean) value); } super.visit(name, value); } @Override public void visitEnum(String name, String desc, String value) { finder.when(value); super.visitEnum(name, desc, value); } @Override public void visitEnd() { if (SUPER_TYPE == how && !finder.whatSpecified()) { finder.what(classNameFromMethodSignature()); } finder.how(how); finder.callback(className, methodName, isStatic); if (finder.isValid()) { finder.scheduleFind(); } super.visitEnd(); } /* * Valid method signature should be something like * (Ljava/lang/Class<Lorg/osgl/aaa/AAAPersistentService;>;)V * And we need to get the type descriptor "Lorg/osgl/aaa/AAAPersistentService;" * from inside */ private String classNameFromMethodSignature() { String descriptor = signature.substring(18); descriptor = descriptor.substring(0, descriptor.length() - 4); if (descriptor.startsWith("+")) { // this is an interface? descriptor = descriptor.substring(1); } return Type.getType(descriptor).getClassName(); } }; } }; } } }