com.google.devtools.build.android.desugar.scan.PrefixReferenceScanner.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.android.desugar.scan.PrefixReferenceScanner.java

Source

// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.android.desugar.scan;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.collect.ImmutableSet;
import javax.annotation.Nullable;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;

/** {@link ClassVisitor} that records references to classes starting with a given prefix. */
class PrefixReferenceScanner extends ClassVisitor {

    /**
     * Returns references with the given prefix in the given class.
     *
     * @param prefix an internal name prefix, typically a package such as {@code com/google/}
     */
    public static ImmutableSet<KeepReference> scan(ClassReader reader, String prefix) {
        PrefixReferenceScanner scanner = new PrefixReferenceScanner(prefix);
        // Frames irrelevant for Android so skip them.  Don't skip debug info in case the class we're
        // visiting has local variable tables (typically it doesn't anyways).
        reader.accept(scanner, ClassReader.SKIP_FRAMES);
        return scanner.roots.build();
    }

    private final ImmutableSet.Builder<KeepReference> roots = ImmutableSet.builder();
    private final PrefixReferenceMethodVisitor mv = new PrefixReferenceMethodVisitor();
    private final PrefixReferenceFieldVisitor fv = new PrefixReferenceFieldVisitor();
    private final PrefixReferenceAnnotationVisitor av = new PrefixReferenceAnnotationVisitor();

    private final String prefix;

    public PrefixReferenceScanner(String prefix) {
        super(Opcodes.ASM6);
        this.prefix = prefix;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        checkArgument(!name.startsWith(prefix));
        if (superName != null) {
            classReference(superName);
        }
        classReferences(interfaces);
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        typeReference(desc);
        return av;
    }

    @Override
    public void visitOuterClass(String owner, String name, String desc) {
        classReference(owner);
        if (desc != null) {
            typeReference(Type.getMethodType(desc));
        }
    }

    @Override
    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
        typeReference(desc);
        return av;
    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        classReference(name);
        if (outerName != null) {
            classReference(outerName);
        }
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        typeReference(desc);
        return fv;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        typeReference(Type.getMethodType(desc));
        classReferences(exceptions);
        return mv;
    }

    private void classReferences(@Nullable String[] internalNames) {
        if (internalNames != null) {
            for (String itf : internalNames) {
                classReference(itf);
            }
        }
    }

    // The following methods are package-private so they don't incur bridge methods when called from
    // inner classes below.

    void classReference(String internalName) {
        checkArgument(internalName.charAt(0) != '[' && internalName.charAt(0) != '(', internalName);
        checkArgument(!internalName.endsWith(";"), internalName);
        if (internalName.startsWith(prefix)) {
            roots.add(KeepReference.classReference(internalName));
        }
    }

    void objectReference(String internalName) {
        // don't call this for method types, convert to Type instead
        checkArgument(internalName.charAt(0) != '(', internalName);
        if (internalName.charAt(0) == '[') {
            typeReference(internalName);
        } else {
            classReference(internalName);
        }
    }

    void typeReference(String typeDesc) {
        // don't call this for method types, convert to Type instead
        checkArgument(typeDesc.charAt(0) != '(', typeDesc);

        int lpos = typeDesc.lastIndexOf('[') + 1;
        if (typeDesc.charAt(lpos) == 'L') {
            checkArgument(typeDesc.endsWith(";"), typeDesc);
            classReference(typeDesc.substring(lpos, typeDesc.length() - 1));
        } else {
            // else primitive or primitive array
            checkArgument(typeDesc.length() == lpos + 1, typeDesc);
            switch (typeDesc.charAt(lpos)) {
            case 'B':
            case 'C':
            case 'S':
            case 'I':
            case 'J':
            case 'D':
            case 'F':
            case 'Z':
                break;
            default:
                throw new AssertionError("Unexpected type descriptor: " + typeDesc);
            }
        }
    }

    void typeReference(Type type) {
        switch (type.getSort()) {
        case Type.ARRAY:
            typeReference(type.getElementType());
            break;
        case Type.OBJECT:
            classReference(type.getInternalName());
            break;

        case Type.METHOD:
            for (Type param : type.getArgumentTypes()) {
                typeReference(param);
            }
            typeReference(type.getReturnType());
            break;

        default:
            break;
        }
    }

    void fieldReference(String owner, String name, String desc) {
        objectReference(owner);
        typeReference(desc);
        if (owner.startsWith(prefix)) {
            roots.add(KeepReference.memberReference(owner, name, desc));
        }
    }

    void methodReference(String owner, String name, String desc) {
        checkArgument(desc.charAt(0) == '(', desc);
        objectReference(owner);
        typeReference(Type.getMethodType(desc));
        if (owner.startsWith(prefix)) {
            roots.add(KeepReference.memberReference(owner, name, desc));
        }
    }

    void handleReference(Handle handle) {
        switch (handle.getTag()) {
        case Opcodes.H_GETFIELD:
        case Opcodes.H_GETSTATIC:
        case Opcodes.H_PUTFIELD:
        case Opcodes.H_PUTSTATIC:
            fieldReference(handle.getOwner(), handle.getName(), handle.getDesc());
            break;

        default:
            methodReference(handle.getOwner(), handle.getName(), handle.getDesc());
            break;
        }
    }

    private class PrefixReferenceMethodVisitor extends MethodVisitor {

        public PrefixReferenceMethodVisitor() {
            super(Opcodes.ASM6);
        }

        @Override
        public AnnotationVisitor visitAnnotationDefault() {
            return av;
        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            typeReference(desc);
            return av;
        }

        @Override
        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            typeReference(desc);
            return av;
        }

        @Override
        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
            typeReference(desc);
            return av;
        }

        @Override
        public void visitTypeInsn(int opcode, String type) {
            objectReference(type);
        }

        @Override
        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
            fieldReference(owner, name, desc);
        }

        @Override
        @SuppressWarnings("deprecation") // Implementing deprecated method to be sure
        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
            visitMethodInsn(opcode, owner, name, desc, opcode == Opcodes.INVOKEINTERFACE);
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
            methodReference(owner, name, desc);
        }

        @Override
        public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
            typeReference(Type.getMethodType(desc));
            handleReference(bsm);
            for (Object bsmArg : bsmArgs) {
                visitConstant(bsmArg);
            }
        }

        @Override
        public void visitLdcInsn(Object cst) {
            visitConstant(cst);
        }

        private void visitConstant(Object cst) {
            if (cst instanceof Type) {
                typeReference((Type) cst);
            } else if (cst instanceof Handle) {
                handleReference((Handle) cst);
            } else {
                // Check for other expected types as javadoc recommends
                checkArgument(cst instanceof String || cst instanceof Integer || cst instanceof Long
                        || cst instanceof Float || cst instanceof Double, "Unexpected constant: ", cst);
            }
        }

        @Override
        public void visitMultiANewArrayInsn(String desc, int dims) {
            typeReference(desc);
        }

        @Override
        public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            typeReference(desc);
            return av;
        }

        @Override
        public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
            if (type != null) {
                classReference(type);
            }
        }

        @Override
        public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc,
                boolean visible) {
            typeReference(desc);
            return av;
        }

        @Override
        public void visitLocalVariable(String name, String desc, String signature, Label start, Label end,
                int index) {
            typeReference(desc);
        }

        @Override
        public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start,
                Label[] end, int[] index, String desc, boolean visible) {
            typeReference(desc);
            return av;
        }
    }

    private class PrefixReferenceFieldVisitor extends FieldVisitor {

        public PrefixReferenceFieldVisitor() {
            super(Opcodes.ASM6);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            typeReference(desc);
            return av;
        }

        @Override
        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
            typeReference(desc);
            return av;
        }
    }

    private class PrefixReferenceAnnotationVisitor extends AnnotationVisitor {

        public PrefixReferenceAnnotationVisitor() {
            super(Opcodes.ASM6);
        }

        @Override
        public void visit(String name, Object value) {
            if (value instanceof Type) {
                typeReference((Type) value);
            }
        }

        @Override
        public void visitEnum(String name, String desc, String value) {
            fieldReference(desc.substring(1, desc.length() - 1), value, desc);
        }

        @Override
        public AnnotationVisitor visitAnnotation(String name, String desc) {
            typeReference(desc);
            return av;
        }

        @Override
        public AnnotationVisitor visitArray(String name) {
            return av;
        }
    }
}