MethodVisitor (final int access, final String n, final String desc, final String signature, final String[] exceptions) { final Ref<Object> defaultValue = Ref.create(); final Set<TypeRepr.ClassType> annotations = new HashSet<>(); final Set<ParamAnnotation> paramAnnotations = new HashSet<>(); processSignature(signature); return new MethodVisitor(ASM_API_VERSION) { @Override public void visitEnd() { if ((access & Opcodes.ACC_SYNTHETIC) == 0 || (access & Opcodes.ACC_BRIDGE) > 0) { myMethods.add(new MethodRepr( myContext, access, myContext.get(n), myContext.get(signature), desc, annotations, paramAnnotations, exceptions, defaultValue.get() )); } } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { final TypeRepr.ClassType annoType = (TypeRepr.ClassType)TypeRepr.getType(myContext, desc); annotations.add(annoType); return new AnnotationCrawler(annoType, "<init>".equals(n) ? ElemType.CONSTRUCTOR : ElemType.METHOD); } @Override public AnnotationVisitor visitAnnotationDefault() { return new AnnotationVisitor(ASM_API_VERSION) { private @Nullable List<Object> myAcc; @Override public void visit(String name, Object value) { collectValue(value); } @Override public void visitEnum(String name, String desc, String value) { collectValue(value); } @Override public AnnotationVisitor visitArray(String name) { myAcc = new SmartList<>(); return this; } @Override public void visitEnd() { if (myAcc != null) { Object[] template = null; if (!myAcc.isEmpty()) { final Object elem = myAcc.get(0); if (elem != null) { template = ArrayUtil.newArray(elem.getClass(), 0); } } defaultValue.set(template != null? myAcc.toArray(template) : myAcc.toArray()); } } private void collectValue(Object value) { if (myAcc != null) { myAcc.add(value); } else { defaultValue.set(value); } } }; } @Override public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { final TypeRepr.ClassType annoType = (TypeRepr.ClassType)TypeRepr.getType(myContext, desc); paramAnnotations.add(new ParamAnnotation(parameter, annoType)); return new AnnotationCrawler(annoType, ElemType.PARAMETER); } @Override public void visitLdcInsn(Object cst) { if (cst instanceof Type) { myUsages.add(UsageRepr.createClassUsage(myContext, myContext.get(((Type)cst).getInternalName()))); } super.visitLdcInsn(cst); } @Override public void visitMultiANewArrayInsn(String desc, int dims) { final TypeRepr.ArrayType typ = (TypeRepr.ArrayType)TypeRepr.getType(myContext, desc); final TypeRepr.AbstractType element = typ.getDeepElementType(); if (element instanceof TypeRepr.ClassType) { final int className = ((TypeRepr.ClassType)element).className; myUsages.add(UsageRepr.createClassUsage(myContext, className)); myUsages.add(UsageRepr.createClassNewUsage(myContext, className)); } typ.updateClassUsages(myContext, myName, myUsages); super.visitMultiANewArrayInsn(desc, dims); } @Override public void visitLocalVariable(String n, String desc, String signature, Label start, Label end, int index) { processSignature(signature); TypeRepr.getType(myContext, desc).updateClassUsages(myContext, myName, myUsages); super.visitLocalVariable(n, desc, signature, start, end, index); } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { if (type != null) { TypeRepr.createClassType(myContext, myContext.get(type)).updateClassUsages(myContext, myName, myUsages); } super.visitTryCatchBlock(start, end, handler, type); } @Override public void visitTypeInsn(int opcode, String type) { final TypeRepr.AbstractType typ = type.startsWith("[")? TypeRepr.getType(myContext, type) : TypeRepr.createClassType(myContext, myContext.get(type)); if (opcode == Opcodes.NEW) { myUsages.add(UsageRepr.createClassUsage(myContext, ((TypeRepr.ClassType)typ).className)); myUsages.add(UsageRepr.createClassNewUsage(myContext, ((TypeRepr.ClassType)typ).className)); final int ktLambdaMarker = type.indexOf(KOTLIN_LAMBDA_USAGE_CLASS_MARKER); if (ktLambdaMarker > 0) { final int ifNameStart = ktLambdaMarker + KOTLIN_LAMBDA_USAGE_CLASS_MARKER.length(); final int ifNameEnd = type.indexOf("$", ifNameStart); if (ifNameEnd > ifNameStart) { myUsages.add(UsageRepr.createClassNewUsage(myContext, myContext.get(type.substring(ifNameStart, ifNameEnd).replace('_', '/')))); } } } else if (opcode == Opcodes.ANEWARRAY) { if (typ instanceof TypeRepr.ClassType) { myUsages.add(UsageRepr.createClassUsage(myContext, ((TypeRepr.ClassType)typ).className)); myUsages.add(UsageRepr.createClassNewUsage(myContext, ((TypeRepr.ClassType)typ).className)); } } typ.updateClassUsages(myContext, myName, myUsages); super.visitTypeInsn(opcode, type); } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { registerFieldUsage(opcode, owner, name, desc); super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { registerMethodUsage(owner, name, desc); super.visitMethodInsn(opcode, owner, name, desc, itf); } @Override public void visitInvokeDynamicInsn(String methodName, String desc, Handle bsm, Object... bsmArgs) { final Type returnType = Type.getReturnType(desc); addClassUsage(TypeRepr.getType(myContext, returnType)); // common args processing for (Object arg : bsmArgs) { if (arg instanceof Type) { final Type type = (Type)arg; if (type.getSort() == Type.METHOD) { for (Type argType : type.getArgumentTypes()) { addClassUsage(TypeRepr.getType(myContext, argType)); } addClassUsage(TypeRepr.getType(myContext, type.getReturnType())); } else { addClassUsage(TypeRepr.getType(myContext, type)); } } else if (arg instanceof Handle) { processMethodHandle((Handle)arg); } } if (LAMBDA_FACTORY_CLASS.equals(bsm.getOwner())) { // This invokeDynamic implements a lambda or method reference usage. // Need to register method usage for the corresponding SAM-type. // First three arguments to the bootstrap methods are provided automatically by VM. // Arguments in args array are expected to be as following: // [0]: Type: Signature and return type of method to be implemented by the function object. // [1]: Handle: implementation method handle // [2]: Type: The signature and return type that should be enforced dynamically at invocation time. May be the same as samMethodType, or may be a specialization of it // [...]: optional additional arguments if (returnType.getSort() == Type.OBJECT && bsmArgs.length >= 3) { if (bsmArgs[0] instanceof Type) { final Type samMethodType = (Type)bsmArgs[0]; if (samMethodType.getSort() == Type.METHOD) { registerMethodUsage(returnType.getInternalName(), methodName, samMethodType.getDescriptor()); // reflect dynamic proxy instantiation with NewClassUsage myUsages.add(UsageRepr.createClassNewUsage(myContext, myContext.get(returnType.getInternalName()))); } } } } super.visitInvokeDynamicInsn(methodName, desc, bsm, bsmArgs); } private void processMethodHandle(Handle handle) { final String memberOwner = handle.getOwner(); if (memberOwner != null && !memberOwner.equals(myClassNameHolder.get())) { // do not register access to own class members final String memberName = handle.getName(); final String memberDescriptor = handle.getDesc(); final int opCode = getFieldAccessOpcode(handle); if (opCode > 0) { registerFieldUsage(opCode, memberOwner, memberName, memberDescriptor); } else { registerMethodUsage(memberOwner, memberName, memberDescriptor); } } } private void registerFieldUsage(int opcode, String owner, String fName, String desc) { final int fieldName = myContext.get(fName); final int fieldOwner = myContext.get(owner); final int descr = myContext.get(desc); if (opcode == Opcodes.PUTFIELD || opcode == Opcodes.PUTSTATIC) { myUsages.add(UsageRepr.createFieldAssignUsage(myContext, fieldName, fieldOwner, descr)); } if (opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC) { addClassUsage(TypeRepr.getType(myContext, descr)); } myUsages.add(UsageRepr.createFieldUsage(myContext, fieldName, fieldOwner, descr)); } private void registerMethodUsage(String owner, String name, @Nullable String desc) { final int methodOwner = myContext.get(owner); final int methodName = myContext.get(name); myUsages.add(UsageRepr.createMetaMethodUsage(myContext, methodName, methodOwner)); if (desc != null) { myUsages.add(UsageRepr.createMethodUsage(myContext, methodName, methodOwner, desc)); addClassUsage(TypeRepr.getType(myContext, Type.getReturnType(desc))); } } private void addClassUsage(final TypeRepr.AbstractType type) { TypeRepr.ClassType classType = null; if (type instanceof TypeRepr.ClassType) { classType = (TypeRepr.ClassType)type; } else if (type instanceof TypeRepr.ArrayType) { final TypeRepr.AbstractType elemType = ((TypeRepr.ArrayType)type).getDeepElementType(); if (elemType instanceof TypeRepr.ClassType) { classType = (TypeRepr.ClassType)elemType; } } if (classType != null) { myUsages.add(UsageRepr.createClassUsage(myContext, classType.className)); } } }; }