org.decojer.cavaj.readers.asm.ReadClassVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.decojer.cavaj.readers.asm.ReadClassVisitor.java

Source

/*
 * $Id$
 *
 * This file is part of the DecoJer project.
 * Copyright (C) 2010-2011  Andr Pankraz
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
    
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
    
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every Java Source Code
 * that is created using DecoJer.
 */
package org.decojer.cavaj.readers.asm;

import static org.decojer.cavaj.readers.asm.ReadUtils.annotateT;

import java.lang.annotation.RetentionPolicy;

import javax.annotation.Nonnull;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import org.decojer.cavaj.model.A;
import org.decojer.cavaj.model.DU;
import org.decojer.cavaj.model.fields.F;
import org.decojer.cavaj.model.methods.M;
import org.decojer.cavaj.model.types.ClassT;
import org.decojer.cavaj.model.types.T;
import org.decojer.cavaj.readers.ReadVisitor;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.TypeReference;

/**
 * ASM read class visitor.
 *
 * @author Andr Pankraz
 */
@Slf4j
public class ReadClassVisitor extends ClassVisitor implements ReadVisitor {

    private A[] as;

    @Getter
    @Nonnull
    private final DU du;

    @Nonnull
    private final ReadAnnotationMemberVisitor readAnnotationMemberVisitor;

    @Nonnull
    private final ReadFieldVisitor readFieldVisitor;

    @Nonnull
    private final ReadMethodVisitor readMethodVisitor;

    @Getter
    private T t;

    /**
     * Constructor.
     *
     * @param du
     *            decompilation unit
     */
    public ReadClassVisitor(@Nonnull final DU du) {
        super(Opcodes.ASM5);
        this.du = du;
        this.readAnnotationMemberVisitor = new ReadAnnotationMemberVisitor(this);
        this.readFieldVisitor = new ReadFieldVisitor(this);
        this.readMethodVisitor = new ReadMethodVisitor(this);
    }

    @Override
    public ReadVisitor getParentVisitor() {
        return null;
    }

    /**
     * Init.
     */
    public void init() {
        this.as = null;
        this.t = null;
    }

    @Override
    public void visit(final int version, final int access, final String name, final String signature,
            final String superName, final String[] interfaces) {
        if (name == null) {
            log.warn("Cannot read type name '" + name + "'!");
            // We can stop further type reading only via an exception in this visitor based system.
            throw new ReadClassStopException();
        }
        final T t = this.du.getT(name);
        if (!t.createTd()) {
            log.warn("Type '" + t + "' already read!");
            // We can stop further type reading only via an exception in this visitor based system.
            throw new ReadClassStopException();
        }
        this.t = t;
        getT().setAccessFlags(access);
        if (superName != null) {
            getT().setSuperT(this.du.getT(superName));
        }
        if (interfaces != null && interfaces.length > 0) {
            final T[] interfaceTs = new T[interfaces.length];
            for (int i = interfaces.length; i-- > 0;) {
                final String interfaceName = interfaces[i];
                assert interfaceName != null;
                interfaceTs[i] = this.du.getT(interfaceName);
            }
            getT().setInterfaceTs(interfaceTs);
        }
        getT().setSignature(signature);

        // fix ASM bug: mixup of minor and major (which is 196653),
        // only JVM 1.1 class files use a minor number (45.3),
        // JDK 1.1 - JDK 1.3 create this version without a target option
        getT().setVersion(version & 0xffff);
    }

    @Override
    public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
        if (this.as == null) {
            this.as = new A[1];
        } else {
            final A[] newAs = new A[this.as.length + 1];
            System.arraycopy(this.as, 0, newAs, 0, this.as.length);
            this.as = newAs;
        }
        this.as[this.as.length - 1] = this.readAnnotationMemberVisitor.init(desc,
                visible ? RetentionPolicy.RUNTIME : RetentionPolicy.CLASS);
        return this.readAnnotationMemberVisitor;
    }

    @Override
    public void visitAttribute(final Attribute attr) {
        if ("Scala".equals(attr.type) || "ScalaSig".equals(attr.type)) {
            getT().setScala();
            return;
        }
        if (!attr.type.equals("org.aspectj.weaver.WeaverState") && !attr.type.equals("OJC")) {
            log.warn(this.t + ": Unknown class attribute tag '" + attr.type + "'!");
        }
    }

    @Override
    public void visitEnd() {
        if (this.as != null) {
            getT().setAs(this.as);
        }
        getT().resolve();
    }

    @Override
    public FieldVisitor visitField(final int access, final String name, final String desc, final String signature,
            final Object value) {
        if (name == null || desc == null) {
            return null;
        }
        final F f = getT().getF(name, desc);
        f.createFd();

        f.setAccessFlags(access);
        f.setSignature(signature);

        f.setValue(value);

        this.readFieldVisitor.init(f);
        return this.readFieldVisitor;
    }

    @Override
    public void visitInnerClass(final String name, final String outerName, final String innerName,
            final int access) {
        if (name == null) {
            return;
        }
        final ClassT innerT = (ClassT) this.du.getT(name);
        if (outerName != null) {
            // set enclosing first for better inner name check
            innerT.setEnclosingT(this.du.getT(outerName));
        }
        innerT.setInnerInfo(innerName, access);
    }

    @Override
    public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature,
            final String[] exceptions) {
        if (name == null || desc == null) {
            return null;
        }
        final M m = getT().getM(name, desc);
        m.createMd();

        m.setAccessFlags(access);
        if (exceptions != null && exceptions.length > 0) {
            final T[] throwsTs = new T[exceptions.length];
            for (int i = exceptions.length; i-- > 0;) {
                final String exception = exceptions[i];
                assert exception != null;
                throwsTs[i] = this.du.getT(exception);
            }
            m.setThrowsTs(throwsTs);
        }
        m.setSignature(signature);

        this.readMethodVisitor.init(m);
        return this.readMethodVisitor;
    }

    @Override
    public void visitOuterClass(final String owner, final String name, final String desc) {
        if (owner == null) {
            assert false;
            return;
        }
        final ClassT enclosingT = (ClassT) this.du.getT(owner);
        if (name == null || desc == null) {
            getT().setEnclosingT(enclosingT);
        } else {
            getT().setEnclosingM(enclosingT.getM(name, desc));
        }
    }

    @Override
    public void visitSource(final String source, final String debug) {
        if (debug != null) {
            // TODO need an example, really useful in the wild?
            // JVM spec: 4.7.11 The SourceDebugExtension Attribute
            log.warn(this.t + ": " + debug);
        }
        getT().setSourceFileName(source);
    }

    @Override
    public AnnotationVisitor visitTypeAnnotation(final int typeRef, final TypePath typePath, final String desc,
            final boolean visible) {
        final A a = this.readAnnotationMemberVisitor.init(desc,
                visible ? RetentionPolicy.RUNTIME : RetentionPolicy.CLASS);
        if (a == null) {
            log.warn(getT() + ": Cannot read annotation for descriptor '" + desc + "'!");
            return null;
        }
        final TypeReference typeReference = new TypeReference(typeRef);
        switch (typeReference.getSort()) {
        case TypeReference.CLASS_EXTENDS: {
            final int superTypeIndex = typeReference.getSuperTypeIndex();
            if (superTypeIndex == -1) {
                // -1: annotation targets extends type
                final T superT = getT().getSuperT();
                if (superT == null) {
                    log.warn(getT() + ": Cannot apply type annotation '" + a + "' to missing super type!");
                    break;
                }
                getT().setSuperT(annotateT(superT, a, typePath));
                break;
            }
            // 0-based interface index
            final T[] interfaceTs = getT().getInterfaceTs();
            if (superTypeIndex <= interfaceTs.length) {
                log.warn(getT() + ": Cannot apply type annotation '" + a + "' to missing interface type at index '"
                        + superTypeIndex + "'!");
                break;
            }
            final T interfaceT = interfaceTs[superTypeIndex];
            assert interfaceT != null;
            interfaceTs[superTypeIndex] = annotateT(interfaceT, a, typePath);
            break;
        }
        case TypeReference.CLASS_TYPE_PARAMETER: {
            final int typeParameterIndex = typeReference.getTypeParameterIndex();
            final T[] typeParams = getT().getTypeParams();
            if (typeParams.length <= typeParameterIndex) {
                log.warn(getT() + ": Cannot apply type annotation '" + a + "' to missing type parameter at index '"
                        + typeParameterIndex + "'!");
                break;
            }
            final T typeParam = typeParams[typeParameterIndex];
            assert typeParam != null;
            typeParams[typeParameterIndex] = annotateT(typeParam, a, typePath);
            break;
        }
        case TypeReference.CLASS_TYPE_PARAMETER_BOUND: {
            final int typeParameterIndex = typeReference.getTypeParameterIndex();
            final int typeParameterBoundIndex = typeReference.getTypeParameterBoundIndex();
            final T t = getT().getTypeParams()[typeParameterIndex];
            if (typeParameterBoundIndex == 0) {
                // 0: annotation targets extends type
                final T superT = t.getSuperT();
                if (superT == null) {
                    log.warn(getT() + ": Cannot apply type annotation '" + a
                            + "' to missing type parameter bound super type!");
                    break;
                }
                t.setSuperT(annotateT(superT, a, typePath));
                break;
            }
            // 1-based interface index
            final T[] interfaceTs = t.getInterfaceTs();
            if (interfaceTs.length <= typeParameterBoundIndex - 1) {
                log.warn(getT() + ": Cannot apply type annotation '" + a + "' to missing interface type at index '"
                        + (typeParameterBoundIndex - 1) + "'!");
                break;
            }
            final T interfaceT = interfaceTs[typeParameterBoundIndex - 1];
            assert interfaceT != null;
            interfaceTs[typeParameterBoundIndex - 1] = annotateT(interfaceT, a, typePath);
            break;
        }
        default:
            log.warn(
                    this.t + ": Unknown type annotation ref sort '0x" + Integer.toHexString(typeReference.getSort())
                            + "' : " + typeRef + " : " + typePath + " : " + desc + " : " + visible);
        }
        return this.readAnnotationMemberVisitor;
    }

}