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

org.gradle.api.internal.tasks.compile.ApiMemberSelector Maven / Gradle / Ivy

There is a newer version: 8.6
Show newest version
/*
 * Copyright 2016 the original author or authors.
 *
 * 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 org.gradle.api.internal.tasks.compile;

import org.gradle.internal.classanalysis.AsmConstants;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.ModuleVisitor;

import java.util.Set;
import java.util.SortedSet;

import static com.google.common.collect.Sets.newTreeSet;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;

/**
 * Visits each {@link Member} of a given class and selects only those members that should
 * be included in the {@link org.gradle.jvm.tasks.api.ApiJar}. Selected API members are
 * delegated to an adapter that determines how to further process those members (e.g.
 * stripping out method bodies), and how to write a new "API class" with them.
 */
public class ApiMemberSelector extends ClassVisitor {

    private final SortedSet methods = newTreeSet();
    private final SortedSet fields = newTreeSet();
    private final SortedSet innerClasses = newTreeSet();

    private final String className;
    private final ClassVisitor apiMemberAdapter;
    private final boolean apiIncludesPackagePrivateMembers;

    private boolean isInnerClass;
    private ClassMember classMember;
    private boolean thisClassIsPrivateInnerClass;

    public ApiMemberSelector(String className, ClassVisitor apiMemberAdapter, boolean apiIncludesPackagePrivateMembers) {
        super(AsmConstants.ASM_LEVEL);
        this.className = className;
        this.apiMemberAdapter = apiMemberAdapter;
        this.apiIncludesPackagePrivateMembers = apiIncludesPackagePrivateMembers;
    }

    public boolean isPrivateInnerClass() {
        return thisClassIsPrivateInnerClass;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        classMember = new ClassMember(version, access, name, signature, superName, interfaces);
        isInnerClass = (access & ACC_SUPER) == ACC_SUPER;
    }

    @Override
    public ModuleVisitor visitModule(String name, int access, String version) {
        return apiMemberAdapter.visitModule(name, access, version);
    }

    @Override
    public void visitEnd() {
        super.visitEnd();

        apiMemberAdapter.visit(
            classMember.getVersion(), classMember.getAccess(), classMember.getName(), classMember.getSignature(),
            classMember.getSuperName(), classMember.getInterfaces());
        visitAnnotationMembers(classMember.getAnnotations());
        for (MethodMember method : methods) {
            MethodVisitor mv = apiMemberAdapter.visitMethod(
                method.getAccess(), method.getName(), method.getTypeDesc(), method.getSignature(),
                method.getExceptions().toArray(new String[0]));
            visitAnnotationMembers(mv, method.getAnnotations());
            visitAnnotationMembers(mv, method.getParameterAnnotations());
            mv.visitEnd();
        }
        for (FieldMember field : fields) {
            FieldVisitor fieldVisitor = apiMemberAdapter.visitField(
                field.getAccess(), field.getName(), field.getTypeDesc(), field.getSignature(), field.getValue());
            visitAnnotationMembers(fieldVisitor, field.getAnnotations());
            fieldVisitor.visitEnd();
        }
        for (InnerClassMember innerClass : innerClasses) {
            apiMemberAdapter.visitInnerClass(
                innerClass.getName(), innerClass.getOuterName(), innerClass.getInnerName(), innerClass.getAccess());
        }
        apiMemberAdapter.visitEnd();
    }

    private void visitAnnotationMembers(Set annotationMembers) {
        for (AnnotationMember annotation : annotationMembers) {
            AnnotationVisitor annotationVisitor =
                apiMemberAdapter.visitAnnotation(annotation.getName(), annotation.isVisible());
            visitAnnotationValues(annotation, annotationVisitor);
        }
    }

    private void visitAnnotationMembers(MethodVisitor mv, Set annotationMembers) {
        for (AnnotationMember annotation : annotationMembers) {
            AnnotationVisitor annotationVisitor;
            if (annotation instanceof ParameterAnnotationMember) {
                annotationVisitor = mv.visitParameterAnnotation(
                    ((ParameterAnnotationMember) annotation).getParameter(), annotation.getName(),
                    annotation.isVisible());
            } else {
                annotationVisitor = mv.visitAnnotation(annotation.getName(), annotation.isVisible());
            }
            visitAnnotationValues(annotation, annotationVisitor);
        }
    }

    private void visitAnnotationMembers(FieldVisitor fv, Set annotationMembers) {
        for (AnnotationMember annotation : annotationMembers) {
            AnnotationVisitor annotationVisitor = fv.visitAnnotation(annotation.getName(), annotation.isVisible());
            visitAnnotationValues(annotation, annotationVisitor);
        }
    }

    private void visitAnnotationValues(AnnotationMember annotation, AnnotationVisitor annotationVisitor) {
        for (AnnotationValue value : annotation.getValues()) {
            visitAnnotationValue(annotationVisitor, value);
        }
        annotationVisitor.visitEnd();
    }

    private void visitAnnotationValue(AnnotationVisitor annotationVisitor, AnnotationValue value) {
        String name = value.getName();
        if (value instanceof EnumAnnotationValue) {
            annotationVisitor.visitEnum(name, ((EnumAnnotationValue) value).getTypeDesc(), (String) value.getValue());
        } else if (value instanceof SimpleAnnotationValue) {
            annotationVisitor.visit(name, value.getValue());
        } else if (value instanceof ArrayAnnotationValue) {
            AnnotationVisitor arrayVisitor = annotationVisitor.visitArray(name);
            AnnotationValue[] values = ((ArrayAnnotationValue) value).getValue();
            for (AnnotationValue annotationValue : values) {
                visitAnnotationValue(arrayVisitor, annotationValue);
            }
            arrayVisitor.visitEnd();
        } else if (value instanceof AnnotationAnnotationValue) {
            AnnotationMember annotation = ((AnnotationAnnotationValue) value).getValue();
            AnnotationVisitor annVisitor = annotationVisitor.visitAnnotation(name, annotation.getName());
            visitAnnotationValues(annotation, annVisitor);
        }
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        AnnotationMember ann = new AnnotationMember(desc, visible);
        classMember.addAnnotation(ann);
        return new SortingAnnotationVisitor(ann, super.visitAnnotation(desc, visible));
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if ("".equals(name)) {
            // discard static initializers
            return null;
        }
        if (isCandidateApiMember(access, apiIncludesPackagePrivateMembers) || ("".equals(name) && isInnerClass)) {
            final MethodMember methodMember = new MethodMember(access, name, desc, signature, exceptions);
            methods.add(methodMember);
            return new MethodVisitor(AsmConstants.ASM_LEVEL) {
                @Override
                public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                    AnnotationMember ann = new AnnotationMember(desc, visible);
                    methodMember.addAnnotation(ann);
                    return new SortingAnnotationVisitor(ann, super.visitAnnotation(desc, visible));
                }

                @Override
                public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
                    ParameterAnnotationMember ann = new ParameterAnnotationMember(desc, visible, parameter);
                    methodMember.addParameterAnnotation(ann);
                    return new SortingAnnotationVisitor(ann, super.visitParameterAnnotation(parameter, desc, visible));
                }
            };
        }
        return null;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        if (isCandidateApiMember(access, apiIncludesPackagePrivateMembers)) {
            Object keepValue = (access & ACC_STATIC) == ACC_STATIC && ((access & ACC_FINAL) == ACC_FINAL) ? value : null;
            final FieldMember fieldMember = new FieldMember(access, name, signature, desc, keepValue);
            fields.add(fieldMember);
            return new FieldVisitor(AsmConstants.ASM_LEVEL) {
                @Override
                public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                    AnnotationMember ann = new AnnotationMember(desc, visible);
                    fieldMember.addAnnotation(ann);
                    return new SortingAnnotationVisitor(ann, super.visitAnnotation(desc, visible));
                }
            };
        }
        return null;
    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        boolean privateInnerClass = (access & ACC_PRIVATE) != 0;
        if (name.equals(className) && privateInnerClass) {
            thisClassIsPrivateInnerClass = true;
        }
        if (outerName == null || innerName == null || privateInnerClass) {
            // A local, anonymous class or a private inner class - ignore the reference
            return;
        }

        if (!apiIncludesPackagePrivateMembers && isPackagePrivateMember(access)) {
            return;
        }

        innerClasses.add(new InnerClassMember(access, name, outerName, innerName));
        super.visitInnerClass(name, outerName, innerName, access);
    }

    public static boolean isCandidateApiMember(int access, boolean apiIncludesPackagePrivateMembers) {
        return isPublicMember(access)
            || isProtectedMember(access)
            || (apiIncludesPackagePrivateMembers && isPackagePrivateMember(access));
    }

    private static boolean isPublicMember(int access) {
        return (access & ACC_PUBLIC) == ACC_PUBLIC;
    }

    private static boolean isProtectedMember(int access) {
        return (access & ACC_PROTECTED) == ACC_PROTECTED;
    }

    private static boolean isPackagePrivateMember(int access) {
        return (access & (ACC_PUBLIC | ACC_PROTECTED | ACC_PRIVATE)) == 0;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy