com.facebook.buck.jvm.java.abi.InnerClassesTable.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.jvm.java.abi.InnerClassesTable.java

Source

/*
 * Copyright 2017-present Facebook, 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.facebook.buck.jvm.java.abi;

import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementScanner8;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

/**
 * Aids in constructing a table of {@link InnerClassNode}s when generating bytecode for a {@link
 * TypeElement}.
 */
public class InnerClassesTable {
    private final DescriptorFactory descriptorFactory;
    private final AccessFlags accessFlagsUtils;

    public InnerClassesTable(DescriptorFactory descriptorFactory, AccessFlags accessFlagsUtils) {
        this.descriptorFactory = descriptorFactory;
        this.accessFlagsUtils = accessFlagsUtils;
    }

    public void reportInnerClassReferences(Element topElement, ClassVisitor visitor) {
        List<TypeElement> enclosingClasses = new ArrayList<>();
        List<TypeElement> memberClasses = new ArrayList<>();
        Set<TypeElement> referencesToInners = new HashSet<>();

        ElementKind elementKind = topElement.getKind();
        if (elementKind.isClass() || elementKind.isInterface()) {
            TypeElement walker = (TypeElement) topElement;
            while (walker.getNestingKind() == NestingKind.MEMBER) {
                enclosingClasses.add(walker);
                walker = (TypeElement) walker.getEnclosingElement();
            }
        }

        ElementScanner8<Void, Void> elementScanner = new ElementScanner8<Void, Void>() {
            @Override
            public Void visitPackage(PackageElement e, Void aVoid) {
                addTypeReferences(e.getAnnotationMirrors());

                // If we're being asked to report inner class references of a package, it really means
                // we're being asked to report inner class references of package-info.java; i.e., just
                // the package annotations. We therefore return without chaining to super to avoid
                // recursing into enclosed elements.
                return null;
            }

            @Override
            public Void visitType(TypeElement e, Void aVoid) {
                if (e != topElement && !memberClasses.contains(e)) {
                    memberClasses.add(e);
                    return null;
                }

                addTypeReferences(e.getAnnotationMirrors());
                e.getTypeParameters().forEach(typeParam -> scan(typeParam, aVoid));
                addTypeReferences(e.getSuperclass());
                e.getInterfaces().forEach(this::addTypeReferences);
                // Members will be visited in the call to super, below

                return super.visitType(e, aVoid);
            }

            @Override
            public Void visitExecutable(ExecutableElement e, Void aVoid) {
                addTypeReferences(e.getAnnotationMirrors());
                e.getTypeParameters().forEach(typeParam -> scan(typeParam, aVoid));
                addTypeReferences(e.getReturnType());
                addTypeReferences(e.getDefaultValue());
                // Parameters will be visited in the call to super, below
                e.getThrownTypes().forEach(this::addTypeReferences);
                return super.visitExecutable(e, aVoid);
            }

            @Override
            public Void visitVariable(VariableElement e, Void aVoid) {
                addTypeReferences(e.getAnnotationMirrors());
                addTypeReferences(e.asType());
                return super.visitVariable(e, aVoid);
            }

            @Override
            public Void visitTypeParameter(TypeParameterElement e, Void aVoid) {
                addTypeReferences(e.getAnnotationMirrors());
                addTypeReferences(e.asType());
                return super.visitTypeParameter(e, aVoid);
            }

            private void addTypeReferences(TypeMirror type) {
                new TypeScanner8<Void, Void>() {
                    @Override
                    public Void scan(@Nullable TypeMirror t, Void aVoid) {
                        if (t == null) {
                            return null;
                        }
                        return super.scan(t, aVoid);
                    }

                    @Override
                    public Void visitDeclared(DeclaredType t, Void aVoid) {
                        TypeElement element = (TypeElement) t.asElement();
                        if (element.getNestingKind() == NestingKind.MEMBER) {
                            referencesToInners.add(element);
                            element.getEnclosingElement().asType().accept(this, null);
                        }

                        return super.visitDeclared(t, aVoid);
                    }
                }.scan(type);
            }

            private void addTypeReferences(List<? extends AnnotationMirror> annotationMirrors) {
                annotationMirrors.forEach(this::addTypeReferences);
            }

            private void addTypeReferences(AnnotationMirror annotationMirror) {
                addTypeReferences(annotationMirror.getAnnotationType());
                annotationMirror.getElementValues().values().forEach(this::addTypeReferences);
            }

            private void addTypeReferences(@Nullable AnnotationValue annotationValue) {
                if (annotationValue == null) {
                    return;
                }
                new AnnotationValueScanner8<Void, Void>() {
                    @Override
                    public Void visitType(TypeMirror t, Void aVoid) {
                        addTypeReferences(t);
                        return super.visitType(t, aVoid);
                    }

                    @Override
                    public Void visitEnumConstant(VariableElement c, Void aVoid) {
                        addTypeReferences(c.asType());
                        return super.visitEnumConstant(c, aVoid);
                    }

                    @Override
                    public Void visitAnnotation(AnnotationMirror a, Void aVoid) {
                        addTypeReferences(a.getAnnotationType());
                        return super.visitAnnotation(a, aVoid);
                    }
                }.scan(annotationValue);
            }
        };
        elementScanner.scan(topElement);

        Set<TypeElement> reported = new HashSet<>();
        for (TypeElement element : Lists.reverse(enclosingClasses)) {
            if (reported.add(element)) {
                visitor.visitInnerClass(descriptorFactory.getInternalName(element),
                        descriptorFactory.getInternalName((TypeElement) element.getEnclosingElement()),
                        element.getSimpleName().toString(),
                        accessFlagsUtils.getAccessFlags(element) & ~Opcodes.ACC_SUPER);
            }
        }

        for (TypeElement element : Lists.reverse(memberClasses)) {
            if (reported.add(element)) {
                visitor.visitInnerClass(descriptorFactory.getInternalName(element),
                        descriptorFactory.getInternalName((TypeElement) element.getEnclosingElement()),
                        element.getSimpleName().toString(),
                        accessFlagsUtils.getAccessFlags(element) & ~Opcodes.ACC_SUPER);
            }
        }

        referencesToInners.stream().filter(reported::add)
                .sorted(Comparator.comparing(e -> e.getQualifiedName().toString())).forEach(element -> {
                    visitor.visitInnerClass(descriptorFactory.getInternalName(element),
                            descriptorFactory.getInternalName((TypeElement) element.getEnclosingElement()),
                            element.getSimpleName().toString(),
                            accessFlagsUtils.getAccessFlags(element) & ~Opcodes.ACC_SUPER);
                });
    }
}