com.devexperts.usages.ClassUsagesAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for com.devexperts.usages.ClassUsagesAnalyzer.java

Source

/*
 * usages - Usages Analysis Tool
 * Copyright (C) 2002-2014  Devexperts LLC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.devexperts.usages;

import org.objectweb.asm.*;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;

class ClassUsagesAnalyzer extends ClassVisitor {
    private static final String INIT_METHOD = "<init>";
    private static final String DEFAULT_ANNOTATION_PROPERTY = "value";

    private final Cache cache;
    private final Usages usages;
    private final String className;

    private static String toClassName(String internalName) {
        return internalName.replace('/', '.');
    }

    public ClassUsagesAnalyzer(Usages usages, String className) {
        super(Opcodes.ASM4);
        this.cache = usages.getCache();
        this.usages = usages;
        this.className = className;
    }

    private void markMemberUse(String className, String name, Member usedFrom, UseKind useKind) {
        if (!Config.excludesClassName(className))
            usages.getUsagesForClass(className).addMemberUsage(name, usedFrom, useKind);
    }

    private void makeTypeUse(String className, Member usedFrom, UseKind useKind) {
        if (!Config.excludesClassName(className))
            usages.getUsagesForClass(className).addTypeUsage(usedFrom, useKind);
    }

    private void markTypeUse(Type type, Member usedFrom, UseKind useKind) {
        if (type.getSort() == Type.METHOD) {
            markTypeUse(type.getReturnType(), usedFrom, UseKind.RETURN);
            for (Type arg : type.getArgumentTypes())
                markTypeUse(arg, usedFrom, UseKind.ARGUMENT);
            return;
        }
        while (type.getSort() == Type.ARRAY)
            type = type.getElementType();
        if (type.getSort() == Type.OBJECT)
            makeTypeUse(type.getClassName(), usedFrom, useKind);
    }

    private void markHandleUse(Handle handle, Member usedFrom, UseKind useKind) {
        markTypeUse(Type.getType(handle.getDesc()), usedFrom, useKind);
        String className = Type.getType(handle.getOwner()).getClassName();
        switch (handle.getTag()) {
        case Opcodes.H_GETFIELD:
        case Opcodes.H_GETSTATIC:
        case Opcodes.H_PUTFIELD:
        case Opcodes.H_PUTSTATIC:
            markMemberUse(className, handle.getName(), usedFrom, useKind);
            break;
        case Opcodes.H_INVOKEVIRTUAL:
        case Opcodes.H_INVOKESTATIC:
        case Opcodes.H_INVOKESPECIAL:
        case Opcodes.H_NEWINVOKESPECIAL:
        case Opcodes.H_INVOKEINTERFACE:
            markMemberUse(className, Member.methodMemberName(handle.getName(), Type.getType(handle.getDesc())),
                    usedFrom, useKind);
        }
    }

    private void markConstant(Object cst, Member usedFrom, UseKind useKind) {
        if (cst instanceof Type)
            markTypeUse((Type) cst, usedFrom, useKind);
        else if (cst instanceof Handle)
            markHandleUse((Handle) cst, usedFrom, useKind);
    }

    private void markSignatureUse(String signature, Member usedFrom) {
        if (signature != null)
            new SignatureReader(signature).accept(new SignatureAnalyzer(usedFrom));
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        Member usedFrom = cache.resolveMember(className, Member.CLASS_MEMBER_NAME);
        makeTypeUse(toClassName(superName), usedFrom, UseKind.EXTEND);
        if (interfaces != null)
            for (String intf : interfaces)
                makeTypeUse(toClassName(intf), usedFrom, UseKind.IMPLEMENT);
        markSignatureUse(signature, usedFrom);
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        Member usedFrom = cache.resolveMember(className, name);
        markTypeUse(Type.getType(desc), usedFrom, UseKind.FIELD);
        markSignatureUse(signature, usedFrom);
        if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0 && ((access & Opcodes.ACC_STATIC) == 0))
            usages.getUsagesForClass(className).addInheritableMember(name);
        return null;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        String methodMemberName = Member.methodMemberName(name, Type.getType(desc));
        Member usedFrom = cache.resolveMember(className, methodMemberName);
        markTypeUse(Type.getType(desc), usedFrom, UseKind.UNKNOWN); // will be replaced by RETURN/ARGUMENT
        markSignatureUse(signature, usedFrom);
        if (exceptions != null)
            for (String ex : exceptions)
                makeTypeUse(toClassName(ex), usedFrom, UseKind.THROW);
        if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0 && ((access & Opcodes.ACC_STATIC) == 0)
                && !name.equals(INIT_METHOD))
            usages.getUsagesForClass(className).addInheritableMember(methodMemberName);
        return new MethodAnalyzer(usedFrom);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        Type annotationType = Type.getType(desc);
        Member usedFrom = cache.resolveMember(className, Member.CLASS_MEMBER_NAME);
        markTypeUse(annotationType, usedFrom, UseKind.ANNOTATION);
        return new AnnotationAnalyzer(annotationType.getClassName(), usedFrom);
    }

    private class MethodAnalyzer extends MethodVisitor {
        private Member method;

        private MethodAnalyzer(Member method) {
            super(Opcodes.ASM4);
            this.method = method;
        }

        @Override
        public void visitTypeInsn(int opcode, String type) {
            UseKind useKind;
            switch (opcode) {
            case Opcodes.NEW:
                useKind = UseKind.NEW;
                break;
            case Opcodes.ANEWARRAY:
                useKind = UseKind.ANEWARRAY;
                break;
            case Opcodes.CHECKCAST:
                useKind = UseKind.CHECKCAST;
                break;
            case Opcodes.INSTANCEOF:
                useKind = UseKind.INSTANCEOF;
                break;
            default:
                useKind = UseKind.UNKNOWN;
            }
            markTypeUse(Type.getObjectType(type), method, useKind);
        }

        @Override
        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
            UseKind useKind;
            switch (opcode) {
            case Opcodes.GETFIELD:
                useKind = UseKind.GETFIELD;
                break;
            case Opcodes.PUTFIELD:
                useKind = UseKind.PUTFIELD;
                break;
            case Opcodes.GETSTATIC:
                useKind = UseKind.GETSTATIC;
                break;
            case Opcodes.PUTSTATIC:
                useKind = UseKind.PUTSTATIC;
                break;
            default:
                useKind = UseKind.UNKNOWN;
            }
            markMemberUse(toClassName(owner), name, method, useKind);
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
            UseKind useKind;
            switch (opcode) {
            case Opcodes.INVOKEVIRTUAL:
                useKind = UseKind.INVOKEVIRTUAL;
                break;
            case Opcodes.INVOKESPECIAL:
                useKind = UseKind.INVOKESPECIAL;
                break;
            case Opcodes.INVOKESTATIC:
                useKind = UseKind.INVOKESTATIC;
                break;
            case Opcodes.INVOKEINTERFACE:
                useKind = UseKind.INVOKEINTERFACE;
                break;
            default:
                useKind = UseKind.UNKNOWN;
            }
            if (!owner.startsWith("["))
                markMemberUse(toClassName(owner), Member.methodMemberName(name, Type.getType(desc)), method,
                        useKind);
        }

        @Override
        public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
            markHandleUse(bsm, method, UseKind.INVOKEDYNAMIC);
            for (Object arg : bsmArgs)
                markConstant(arg, method, UseKind.INVOKEDYNAMIC);
        }

        @Override
        public void visitLdcInsn(Object cst) {
            markConstant(cst, method, UseKind.CONSTANT);
        }

        @Override
        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
            if (type != null)
                makeTypeUse(toClassName(type), method, UseKind.CATCH);
        }

        @Override
        public AnnotationVisitor visitAnnotationDefault() {
            return new AnnotationAnalyzer(className, method);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            return new AnnotationAnalyzer(Type.getType(desc).getClassName(), method);
        }

        @Override
        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
            return new AnnotationAnalyzer(Type.getType(desc).getClassName(), method);
        }
    }

    private class SignatureAnalyzer extends SignatureVisitor {
        private final Member usedFrom;

        private SignatureAnalyzer(Member usedFrom) {
            super(Opcodes.ASM4);
            this.usedFrom = usedFrom;
        }

        @Override
        public void visitClassType(String name) {
            makeTypeUse(toClassName(name), usedFrom, UseKind.SIGNATURE);
        }
    }

    private class AnnotationAnalyzer extends AnnotationVisitor {
        private final String annotationClassName;
        private final Member usedFrom;

        public AnnotationAnalyzer(String annotationClassName, Member usedFrom) {
            super(Opcodes.ASM4);
            this.annotationClassName = annotationClassName;
            this.usedFrom = usedFrom;
        }

        @Override
        public void visit(String name, Object value) {
            markMemberUse(annotationClassName, annotationMemberName(name), usedFrom, UseKind.ANNOTATION);
            markConstant(value, usedFrom, UseKind.ANNOTATION);
        }

        @Override
        public void visitEnum(String name, String desc, String value) {
            markMemberUse(annotationClassName, annotationMemberName(name), usedFrom, UseKind.ANNOTATION);
            markMemberUse(Type.getType(desc).getClassName(), value, usedFrom, UseKind.ANNOTATION);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String name, String desc) {
            markMemberUse(annotationClassName, annotationMemberName(name), usedFrom, UseKind.ANNOTATION);
            markTypeUse(Type.getType(desc), usedFrom, UseKind.ANNOTATION);
            return this;
        }

        @Override
        public AnnotationVisitor visitArray(String name) {
            markMemberUse(annotationClassName, annotationMemberName(name), usedFrom, UseKind.ANNOTATION);
            return this;
        }

        private String annotationMemberName(String name) {
            return (name == null ? DEFAULT_ANNOTATION_PROPERTY : name) + "()";
        }
    }
}