com.facebook.buck.java.abi.ClassMirror.java Source code

Java tutorial

Introduction

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

Source

/*
 * Copyright 2014-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.java.abi;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Sets;
import com.google.common.io.ByteSource;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.IOException;
import java.util.SortedSet;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;

import javax.annotation.Nullable;

class ClassMirror extends ClassVisitor implements Comparable<ClassMirror> {

    private final String fileName;
    private final SortedSet<AnnotationMirror> annotations;
    private final SortedSet<FieldMirror> fields;
    private final SortedSet<InnerClass> innerClasses;
    private final SortedSet<MethodMirror> methods;
    @Nullable
    private OuterClass outerClass;
    private int version;
    private int access;
    @Nullable
    private String signature;
    @Nullable
    private String[] interfaces;
    @Nullable
    private String superName;
    @Nullable
    private String name;

    public ClassMirror(String name) {
        super(Opcodes.ASM5);

        this.fileName = name;
        this.annotations = Sets.newTreeSet();
        this.fields = Sets.newTreeSet();
        this.innerClasses = Sets.newTreeSet();
        this.methods = Sets.newTreeSet();
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        this.name = name;
        this.version = version;
        this.access = access;
        this.signature = signature;
        this.interfaces = interfaces;
        this.superName = superName;
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        AnnotationMirror mirror = new AnnotationMirror(desc, visible);
        annotations.add(mirror);
        return mirror;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        if ((access & Opcodes.ACC_PRIVATE) > 0) {
            return super.visitField(access, name, desc, signature, value);
        }

        FieldMirror mirror = new FieldMirror(access, name, desc, signature, value);
        fields.add(mirror);
        return mirror;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

        if ((access & Opcodes.ACC_PRIVATE) > 0) {
            return super.visitMethod(access, name, desc, signature, exceptions);
        }

        // Bridge methods are created by the compiler, and don't appear in source. It would be nice to
        // skip them, but they're used by the compiler to cover the fact that type erasure has occurred.
        // Normally the compiler adds these as public methods, but if you're compiling against a stub
        // produced using our ABI generator, we don't want people calling it accidentally. Oh well, I
        // guess it happens IRL too.
        //
        // Synthetic methods are also generated by the compiler, unless it's one of the methods named in
        // section 4.7.8 of the JVM spec, which are "<init>" and "Enum.valueOf()" and "Enum.values".
        // None of these are actually harmful to the ABI, so we allow synthetic methods through.
        // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.8
        MethodMirror mirror = new MethodMirror(access, name, desc, signature, exceptions);
        methods.add(mirror);

        return mirror;
    }

    @Override
    public void visitOuterClass(String owner, String name, String desc) {
        outerClass = new OuterClass(owner, name, desc);
        super.visitOuterClass(owner, name, desc);
    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        if ((access & Opcodes.ACC_PRIVATE) > 0) {
            return;
        }

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

    @Override
    public int compareTo(ClassMirror o) {
        return fileName.compareTo(o.fileName);
    }

    public void writeTo(JarOutputStream jar) throws IOException {
        JarEntry entry = new JarEntry(fileName);
        entry.setTime(0);

        jar.putNextEntry(entry);
        ClassWriter writer = new ClassWriter(0);
        writer.visit(version, access, name, signature, superName, interfaces);

        if (outerClass != null) {
            writer.visitOuterClass(outerClass.owner, outerClass.name, outerClass.desc);
        }

        for (InnerClass inner : innerClasses) {
            writer.visitInnerClass(inner.name, inner.outerName, inner.innerName, inner.access);
        }

        for (AnnotationMirror annotation : annotations) {
            annotation.appendTo(writer);
        }

        for (FieldMirror field : fields) {
            field.accept(writer);
        }

        for (MethodMirror method : methods) {
            method.appendTo(writer);
        }
        writer.visitEnd();
        ByteSource.wrap(writer.toByteArray()).copyTo(jar);
        jar.closeEntry();
    }

    private static class InnerClass implements Comparable<InnerClass> {

        private final String name;
        @Nullable
        private final String outerName;
        @Nullable
        private final String innerName;
        private final int access;

        public InnerClass(String name, @Nullable String outerName, @Nullable String innerName, int access) {
            this.name = name;
            this.outerName = outerName;
            this.innerName = innerName;
            this.access = access;
        }

        @Override
        public int compareTo(InnerClass o) {
            Preconditions.checkNotNull(o);
            return ComparisonChain.start().compare(name, o.name)
                    .compare(Strings.nullToEmpty(outerName), Strings.nullToEmpty(o.outerName))
                    .compare(Strings.nullToEmpty(innerName), Strings.nullToEmpty(o.innerName)).result();
        }
    }

    private static class OuterClass implements Comparable<OuterClass> {

        private final String owner;
        private final String name;
        private final String desc;

        public OuterClass(String owner, String name, String desc) {
            this.owner = owner;
            this.name = name;
            this.desc = desc;
        }

        @Override
        public int compareTo(OuterClass o) {
            Preconditions.checkNotNull(o);
            return ComparisonChain.start().compare(owner, o.owner).compare(name, o.name).compare(desc, o.desc)
                    .result();
        }
    }
}