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

Java tutorial

Introduction

Here is the source code for com.facebook.buck.jvm.java.abi.ClassVisitorDriverFromElement.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.base.Preconditions;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementScanner8;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

class ClassVisitorDriverFromElement {
    private final DescriptorFactory descriptorFactory;
    private final SignatureFactory signatureFactory;
    private final SourceVersion targetVersion;
    private final Elements elements;
    private final AccessFlags accessFlagsUtils;
    private final InnerClassesTable innerClassesTable;

    /**
     * @param targetVersion the class file version to target, expressed as the corresponding Java
     *     source version
     */
    ClassVisitorDriverFromElement(SourceVersion targetVersion, Elements elements) {
        this.targetVersion = targetVersion;
        this.elements = elements;
        descriptorFactory = new DescriptorFactory(elements);
        signatureFactory = new SignatureFactory(descriptorFactory);
        accessFlagsUtils = new AccessFlags(elements);
        innerClassesTable = new InnerClassesTable(descriptorFactory, accessFlagsUtils);
    }

    public void driveVisitor(Element fullElement, ClassVisitor visitor) {
        fullElement.accept(new ElementVisitorAdapter(), visitor);
        visitor.visitEnd();
    }

    /** Gets the class file version corresponding to the given source version constant. */
    private static int sourceVersionToClassFileVersion(SourceVersion version) {
        switch (version) {
        case RELEASE_0:
            return Opcodes.V1_1; // JVMS8 4.1: 1.0 and 1.1 both support version 45.3 (Opcodes.V1_1)
        case RELEASE_1:
            return Opcodes.V1_1;
        case RELEASE_2:
            return Opcodes.V1_2;
        case RELEASE_3:
            return Opcodes.V1_3;
        case RELEASE_4:
            return Opcodes.V1_4;
        case RELEASE_5:
            return Opcodes.V1_5;
        case RELEASE_6:
            return Opcodes.V1_6;
        case RELEASE_7:
            return Opcodes.V1_7;
        case RELEASE_8:
            return Opcodes.V1_8;
        default:
            throw new IllegalArgumentException(String.format("Unexpected source version: %s", version));
        }
    }

    private interface VisitorWithAnnotations {
        AnnotationVisitor visitAnnotation(String desc, boolean visible);
    }

    private class ElementVisitorAdapter extends ElementScanner8<Void, ClassVisitor> {
        boolean classVisitorStarted = false;

        @Override
        public Void visitPackage(PackageElement e, ClassVisitor classVisitor) {
            classVisitor.visit(sourceVersionToClassFileVersion(targetVersion),
                    Opcodes.ACC_SYNTHETIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
                    e.getQualifiedName().toString().replace('.', '/') + "/package-info", null, "java/lang/Object",
                    new String[0]);

            visitAnnotations(e.getAnnotationMirrors(), classVisitor::visitAnnotation);

            innerClassesTable.reportInnerClassReferences(e, classVisitor);

            classVisitor.visitEnd();

            return null;
        }

        // TODO(jkeljo): Type annotations

        @Override
        public Void visitType(TypeElement e, ClassVisitor visitor) {
            if (classVisitorStarted) {
                // We'll get inner class references later
                return null;
            }

            TypeMirror superclass = e.getSuperclass();
            if (superclass.getKind() == TypeKind.NONE) {
                superclass = Preconditions.checkNotNull(elements.getTypeElement("java.lang.Object")).asType();
            }

            visitor.visit(sourceVersionToClassFileVersion(targetVersion),
                    accessFlagsUtils.getAccessFlagsForClassNode(e), descriptorFactory.getInternalName(e),
                    signatureFactory.getSignature(e), descriptorFactory.getInternalName(superclass),
                    e.getInterfaces().stream().map(descriptorFactory::getInternalName)
                            .toArray(size -> new String[size]));
            classVisitorStarted = true;

            visitAnnotations(e.getAnnotationMirrors(), visitor::visitAnnotation);

            super.visitType(e, visitor);

            innerClassesTable.reportInnerClassReferences(e, visitor);

            return null;
        }

        @Override
        public Void visitExecutable(ExecutableElement e, ClassVisitor visitor) {
            if (e.getModifiers().contains(Modifier.PRIVATE)) {
                return null;
            }

            // TODO(jkeljo): Bridge methods: Look at superclasses, then interfaces, checking whether
            // method types change in the new class

            String[] exceptions = e.getThrownTypes().stream().map(descriptorFactory::getInternalName)
                    .toArray(count -> new String[count]);

            MethodVisitor methodVisitor = visitor.visitMethod(accessFlagsUtils.getAccessFlags(e),
                    e.getSimpleName().toString(), descriptorFactory.getDescriptor(e),
                    signatureFactory.getSignature(e), exceptions);

            visitParameters(e.getParameters(), methodVisitor, MoreElements.isInnerClassConstructor(e));
            visitDefaultValue(e, methodVisitor);
            visitAnnotations(e.getAnnotationMirrors(), methodVisitor::visitAnnotation);
            methodVisitor.visitEnd();

            return null;
        }

        private void visitParameters(List<? extends VariableElement> parameters, MethodVisitor methodVisitor,
                boolean isInnerClassConstructor) {
            if (isInnerClassConstructor) {
                // ASM uses a fake annotation to indicate synthetic parameters
                methodVisitor.visitParameterAnnotation(0, "Ljava/lang/Synthetic;", false);
            }
            for (int i = 0; i < parameters.size(); i++) {
                VariableElement parameter = parameters.get(i);
                for (AnnotationMirror annotationMirror : parameter.getAnnotationMirrors()) {
                    if (MoreElements.isSourceRetention(annotationMirror)) {
                        continue;
                    }
                    visitAnnotationValues(annotationMirror,
                            methodVisitor.visitParameterAnnotation(isInnerClassConstructor ? i + 1 : i,
                                    descriptorFactory.getDescriptor(annotationMirror.getAnnotationType()),
                                    MoreElements.isRuntimeRetention(annotationMirror)));
                }
            }
        }

        private void visitDefaultValue(ExecutableElement e, MethodVisitor methodVisitor) {
            AnnotationValue defaultValue = e.getDefaultValue();
            if (defaultValue == null) {
                return;
            }

            AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault();
            visitAnnotationValue(null, defaultValue, annotationVisitor);
            annotationVisitor.visitEnd();
        }

        @Override
        public Void visitVariable(VariableElement e, ClassVisitor classVisitor) {
            if (e.getModifiers().contains(Modifier.PRIVATE)) {
                return null;
            }

            FieldVisitor fieldVisitor = classVisitor.visitField(accessFlagsUtils.getAccessFlags(e),
                    e.getSimpleName().toString(), descriptorFactory.getDescriptor(e),
                    signatureFactory.getSignature(e), e.getConstantValue());
            visitAnnotations(e.getAnnotationMirrors(), fieldVisitor::visitAnnotation);
            fieldVisitor.visitEnd();

            return null;
        }

        private void visitAnnotations(List<? extends AnnotationMirror> annotations,
                VisitorWithAnnotations visitor) {
            annotations.forEach(annotation -> visitAnnotation(annotation, visitor));
        }

        private void visitAnnotation(AnnotationMirror annotation, VisitorWithAnnotations visitor) {
            if (MoreElements.isSourceRetention(annotation)) {
                return;
            }
            AnnotationVisitor annotationVisitor = visitor.visitAnnotation(
                    descriptorFactory.getDescriptor(annotation.getAnnotationType()),
                    MoreElements.isRuntimeRetention(annotation));
            visitAnnotationValues(annotation, annotationVisitor);
            annotationVisitor.visitEnd();
        }

        private void visitAnnotationValues(AnnotationMirror annotation, AnnotationVisitor annotationVisitor) {
            visitAnnotationValues(annotation.getElementValues(), annotationVisitor);
        }

        private void visitAnnotationValues(
                Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues,
                AnnotationVisitor visitor) {
            elementValues.entrySet()
                    .forEach(entry -> visitAnnotationValue(entry.getKey().getSimpleName().toString(),
                            entry.getValue(), visitor));
        }

        private void visitAnnotationValue(@Nullable String name, AnnotationValue value, AnnotationVisitor visitor) {
            value.accept(new AnnotationVisitorAdapter(name, visitor), null);
        }

        private class AnnotationVisitorAdapter extends SimpleAnnotationValueVisitor8<Void, Void> {
            @Nullable
            private final String name;
            private final AnnotationVisitor visitor;

            private AnnotationVisitorAdapter(@Nullable String name, AnnotationVisitor visitor) {
                this.name = name;
                this.visitor = visitor;
            }

            @Override
            protected Void defaultAction(Object value, Void aVoid) {
                visitor.visit(name, value);
                return null;
            }

            @Override
            public Void visitType(TypeMirror value, Void aVoid) {
                visitor.visit(name, descriptorFactory.getType(value));
                return null;
            }

            @Override
            public Void visitEnumConstant(VariableElement value, Void aVoid) {
                visitor.visitEnum(name, descriptorFactory.getDescriptor(value.getEnclosingElement().asType()),
                        value.getSimpleName().toString());
                return null;
            }

            @Override
            public Void visitAnnotation(AnnotationMirror value, Void aVoid) {
                AnnotationVisitor annotationValueVisitor = visitor.visitAnnotation(name,
                        descriptorFactory.getDescriptor(value.getAnnotationType()));
                visitAnnotationValues(value, annotationValueVisitor);
                annotationValueVisitor.visitEnd();
                return null;
            }

            @Override
            public Void visitArray(List<? extends AnnotationValue> listValue, Void aVoid) {
                AnnotationVisitor arrayMemberVisitor = visitor.visitArray(name);
                listValue.forEach(
                        annotationValue -> visitAnnotationValue(null, annotationValue, arrayMemberVisitor));
                arrayMemberVisitor.visitEnd();
                return null;
            }
        }
    }
}