All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.nebula.lint.rule.dependency.DependencyClassVisitor.groovy Maven / Gradle / Ivy

Go to download

Pluggable and configurable linter tool for identifying and reporting on patterns of misuse or deprecations in Gradle scripts

There is a newer version: 20.2.2
Show newest version
/*
 * Copyright 2015-2019 Netflix, Inc.
 *
 * 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 com.netflix.nebula.lint.rule.dependency

import org.gradle.api.artifacts.ResolvedArtifact
import org.objectweb.asm.*
import org.objectweb.asm.signature.SignatureReader
import org.objectweb.asm.signature.SignatureVisitor
import org.slf4j.Logger
import org.slf4j.LoggerFactory

final class DependencyClassVisitor extends ClassVisitor {
    private Map> classOwners
    private String className
    private ClassLoader loader
    private Logger logger = LoggerFactory.getLogger(DependencyClassVisitor)

    Set directReferences = new HashSet()

    /**
     * References that are necessary at compile time (e.g. type hierarchy of implemented interfaces), but are satisfactory
     * as transitive dependencies. Example: guice's GuiceServletContextListener refers to javax.servlet's
     * ServletContextListener, but adding guice as a dependency does NOT result in a transitive dependency on
     * javax.servlet. In this case, we want to preserve a first order dependency on javax.servlet when
     * GuiceServletContextListener is extended somewhere in the code.
     */
    Set indirectReferences = new HashSet()

    DependencyClassVisitor(Map> classOwners, ClassLoader loader) {
        super(Opcodes.ASM7)
        this.classOwners = classOwners
        this.loader = loader
    }

    void readSignature(String signature) {
        if(signature)
            new SignatureReader(signature).accept(new DependencySignatureVisitor())
    }

    void readObjectName(String type, boolean indirect = false) {
        if(!type) return
        def owners = classOwners[Type.getObjectType(type).internalName] ?: Collections.emptySet()
        if(logger.isDebugEnabled()) {
            for (owner in owners) {
                logger.debug("$className refers to $type which was found in $owner")
            }
        }

        if(indirect) indirectReferences.addAll(owners)
        else directReferences.addAll(owners)
    }

    void readType(String desc) {
        if(!desc) return
        def t = Type.getType(desc)
        switch(t.sort) {
            case Type.ARRAY:
                readType(t.elementType.descriptor)
                break
            case Type.OBJECT:
                readObjectName(t.internalName)
                break
            default:
                readObjectName(desc)
        }
    }

    @Override
    void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        className = name
        readObjectName(superName)
        interfaces.each { readObjectName(it) }

        if(superName) {
            try {
                ClassHierarchyUtils.typeHierarchy(Class.forName(superName.replace('/', '.'), false, loader)).each {
                    readObjectName(it.replace('.', '/'), true)
                }
            } catch(ClassNotFoundException ignored) {
                // do nothing
            } catch(NoClassDefFoundError ignored) {
                // do nothing
            }
        }
        interfaces.each { intf ->
            try {
                ClassHierarchyUtils.typeHierarchy(Class.forName(intf.replace('/', '.'), false, loader)).each {
                    readObjectName(it.replace('.', '/'), true)
                }
            } catch(ClassNotFoundException ignored) {
                // do nothing
            }
        }

        readSignature(signature)
    }

    @Override
    AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
        readType(desc)
        return new DependencyAnnotationVisitor()
    }

    @Override
    AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        readType(desc)
        return new DependencyAnnotationVisitor()
    }

    @Override
    FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        readType(desc)
        readSignature(signature)
        return new DependencyFieldVisitor()
    }

    @Override
    MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        Type.getArgumentTypes(desc).each { readType(it.descriptor) }
        readType(Type.getReturnType(desc).descriptor)
        readSignature(signature)
        exceptions.each { readObjectName(it) }
        return new DependencyMethodVisitor()
    }

    class DependencySignatureVisitor extends SignatureVisitor {
        DependencySignatureVisitor() {
            super(Opcodes.ASM7)
        }

        @Override void visitClassType(String name) { readObjectName(name) }

        @Override SignatureVisitor visitInterfaceBound() { this }
        @Override SignatureVisitor visitClassBound() { this }
        @Override SignatureVisitor visitReturnType() { this }
        @Override SignatureVisitor visitParameterType() { this }
        @Override SignatureVisitor visitExceptionType() { this }
        @Override SignatureVisitor visitArrayType() { this }
    }

    class DependencyFieldVisitor extends FieldVisitor {
        DependencyFieldVisitor() {
            super(Opcodes.ASM7)
        }

        @Override
        AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            readType(desc)
            return new DependencyAnnotationVisitor()
        }

        @Override
        AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            readType(desc)
            return new DependencyAnnotationVisitor()
        }
    }

    class DependencyMethodVisitor extends MethodVisitor {
        DependencyMethodVisitor() {
            super(Opcodes.ASM7)
        }

        @Override
        void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
            readObjectName(owner)
            readType(Type.getReturnType(desc).descriptor)
            Type.getArgumentTypes(desc).collect { readType(it.descriptor) }
        }

        @Override
        void visitFieldInsn(int opcode, String owner, String name, String desc) {
            readType(desc)
        }

        @Override
        void visitTypeInsn(int opcode, String type) {
            readObjectName(type)
        }

        @Override
        void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
            readType(desc)
        }

        @Override
        AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
            readType(desc)
            return new DependencyAnnotationVisitor()
        }

        @Override
        AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            readType(desc)
            return new DependencyAnnotationVisitor()
        }

        @Override
        AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            readType(desc)
            return super.visitAnnotation(desc, visible)
        }

        @Override
        void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
            readObjectName(type)
        }

        @Override
        AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            readType(desc)
            return new DependencyAnnotationVisitor()
        }

        @Override
        void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
            readType(desc)
            readSignature(signature)
        }

        @Override
        AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) {
            readType(desc)
            return new DependencyAnnotationVisitor()
        }

        @Override
        AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            readType(desc)
            return new DependencyAnnotationVisitor()
        }

        @Override
        void visitMultiANewArrayInsn(String desc, int dims) {
            readType(desc)
        }

        @Override
        void visitLdcInsn(Object cst) {
            if(cst instanceof Type) {
                readObjectName(cst.internalName)
            }
        }
    }

    class DependencyAnnotationVisitor extends AnnotationVisitor {
        DependencyAnnotationVisitor() {
            super(Opcodes.ASM7)
        }

        @Override
        void visit(String name, Object value) {
            if(value instanceof Type)
                readObjectName(value.internalName)
        }

        @Override
        void visitEnum(String name, String desc, String value) {
            readType(desc)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy