asmlib.InfoMethod.java Source code

Java tutorial

Introduction

Here is the source code for asmlib.InfoMethod.java

Source

/*
 * asmlib: a toolkit based on ASM for working with java bytecode
 * Copyright (C) 2015 Ivo Anjo <ivo.anjo@ist.utl.pt>
 *
 * This file is part of asmlib.
 *
 * asmlib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * asmlib 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with asmlib.  If not, see <http://www.gnu.org/licenses/>.
 */

package asmlib;

import java.util.*;
import org.objectweb.asm.Opcodes;

import util.StringList;
import util.UtilList;
import asmlib.extra.InvokedMethod;

public class InfoMethod implements Comparable<InfoMethod> {

    /** Comparador que ignora a classe a qual o mtodo pertence **/
    public final static class InfoMethodNameDescOnlyComparator implements Comparator<InfoMethod> {
        @Override
        public int compare(InfoMethod m1, InfoMethod m2) {
            return (m1.name() + m1.desc()).compareTo(m2.name() + m2.desc());
        }
    }

    private final int _access;
    private final String _name;
    private final String _desc;
    private final String _signature;
    private final String[] _exceptions;
    private final List<InfoAnnotation> _annotations = new ArrayList<InfoAnnotation>();
    private final InfoClass _definedInfoClass; // Classe onde o mtodo est realmente definido
    private InfoClass _infoClass; // Classe a partir de onde o mtodo est a ser visto
    private boolean _accessesOutsideFields = false;

    private int _maxLocals;

    public InfoMethod(int access, String name, String desc, String signature, String[] exceptions,
            InfoClass infoClass) {
        _access = access;
        _name = name;
        _desc = desc;
        _signature = signature;
        _exceptions = exceptions;
        _definedInfoClass = infoClass;
        _infoClass = infoClass;
    }

    public InfoMethod(InfoMethod other, InfoClass infoClass) {
        this(other.access(), other.name(), other.desc(), other.signature(), other.exceptions(), other.infoClass());
        _infoClass = infoClass;
        addInvokedMethod(new InvokedMethod(other.infoClass().type(), name(), desc(), other));
    }

    public int access() {
        return _access;
    }

    public String name() {
        return _name;
    }

    public String fullName() {
        return infoClass().type() + "." + name() + desc();
    }

    public String desc() {
        return _desc;
    }

    public String signature() {
        return _signature;
    }

    public String[] exceptions() {
        return _exceptions;
    }

    public InfoClass definedInfoClass() {
        return _definedInfoClass;
    }

    public InfoClass infoClass() {
        return _infoClass;
    }

    public String fullJavaName() {
        Type t = returnType();
        return (t.isPrimitive() ? t.primitiveTypeName() : t.commonName()) + " " + infoClass().type() + "." + name()
                + javaDesc();
    }

    public int maxLocals() {
        return _maxLocals;
    }

    public void setMaxLocals(int maxLocals) {
        _maxLocals = maxLocals;
    }

    public void addAnnotation(InfoAnnotation annot) {
        _annotations.add(annot);
    }

    public List<InfoAnnotation> annotations() {
        return Collections.unmodifiableList(_annotations);
    }

    public boolean hasAnnotation(Type annotClass) {
        return _annotations.contains(new InfoAnnotation(annotClass));
    }

    public InfoAnnotation getAnnotation(Type annotClass) {
        InfoAnnotation target = new InfoAnnotation(annotClass);
        for (InfoAnnotation annot : _annotations) {
            if (annot.equals(target))
                return annot;
        }
        return null;
    }

    @Override
    public String toString() {
        return "InfoMethod (name=" + name() + " desc=" + desc()
        /*+ " signature=" + signature()*/ /*+ " annotations="
                                          + annotations()*/ + " class="
                + (infoClass() != null ? infoClass().type() : null) + " native=" + isNative() + ")";
    }

    public void setAccessesOutsideFields(boolean b) {
        _accessesOutsideFields = b;
    }

    public boolean accessesOutsideFields() {
        return _accessesOutsideFields;
    }

    public boolean isNative() {
        return (access() & Opcodes.ACC_NATIVE) != 0;
    }

    public boolean isAbstract() {
        return (access() & Opcodes.ACC_ABSTRACT) != 0;
    }

    public boolean isFinal() {
        // Mtodo  final se for declarado final, ou se a classe onde est declarado for final
        return ((access() & Opcodes.ACC_FINAL) != 0) || infoClass().isFinal();
    }

    public boolean isPrivate() {
        return (access() & Opcodes.ACC_PRIVATE) != 0;
    }

    public boolean isSynchronized() {
        return (access() & Opcodes.ACC_SYNCHRONIZED) != 0;
    }

    @Override
    public int hashCode() {
        return (infoClass().type() + name() + desc()).hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof InfoMethod) {
            InfoMethod other = (InfoMethod) o;
            return compareTo(other) == 0;
        }
        return false;
    }

    public int compareTo(InfoMethod other) {
        return (infoClass().type() + "." + name() + desc())
                .compareTo(other.infoClass().type() + "." + other.name() + other.desc());
    }

    private Set<InvokedMethod> _invokedMethods = new HashSet<InvokedMethod>();
    private List<InvokedMethod> _invokedMethodsList = new ArrayList<InvokedMethod>();

    public void addInvokedMethod(InvokedMethod m) {
        _invokedMethods.add(m);
        _invokedMethodsList.add(m);
    }

    public List<InvokedMethod> invokedMethodsSet() {
        return new ArrayList<InvokedMethod>(_invokedMethods);
    }

    public List<InvokedMethod> invokedMethodsList() {
        return new ArrayList<InvokedMethod>(_invokedMethodsList);
    }

    /** Se uma UnwindException est a ser leaked para o exterior, significa que h bug no mtodo chamado
      * pois estas nunca devem sair desta classe, portanto esta excepo serve apenas para detectar bugs
      * no InfoMethod, no algum erro da parte de quem o chama.
      **/
    static class UnwindException extends Error {
        private static final long serialVersionUID = 1L;
    }

    private boolean _invokesNativeWorking = false;

    /** ATENO: TOTALMENTE NO THREAD-SAFE. **/
    // Nota: Este mtodo no tem em conta subclasses (overrides), logo invokesNative == false no
    // implica que uma chamada pelo mtodo no seja native, porque pode ir parar a uma subclasse.
    // Ver canInvokeNative()
    public boolean invokesNative() {
        if (isNative())
            return true;

        // Ter cuidado com isto, porque pode causar ciclos
        if (_invokesNativeWorking)
            throw new UnwindException();
        _invokesNativeWorking = true;

        for (InvokedMethod im : _invokedMethods) {
            try {
                if (im.method().invokesNative()) {
                    _invokesNativeWorking = false;
                    return true;
                }
            } catch (UnwindException e) {
            }
        }
        _invokesNativeWorking = false;
        return false;
    }

    /** ATENO: TOTALMENTE NO THREAD-SAFE. **/
    // Considera tambm subclasses que estejam no reachableMap, ou todas as subclasses caso
    // reachableMap esteja a null, na sua resposta.
    public boolean canInvokeNative(Map<Type, InfoClass> reachableMap) {
        if (isNative())
            return true;

        // Ter cuidado com isto, porque pode causar ciclos
        if (_invokesNativeWorking)
            throw new UnwindException();
        _invokesNativeWorking = true;

        for (InvokedMethod im : _invokedMethods)
            try {
                if (im.method().canInvokeNative(reachableMap)) {
                    _invokesNativeWorking = false;
                    return true;
                }
            } catch (UnwindException e) {
            }

        // Constructores no podem ser overridden
        if (name().equals("<init>")) {
            _invokesNativeWorking = false;
            return false;
        }

        for (InfoMethod m : subclassOverrides(reachableMap))
            try {
                if (m.canInvokeNative(reachableMap)) {
                    _invokesNativeWorking = false;
                    return true;
                }
            } catch (UnwindException e) {
            }

        _invokesNativeWorking = false;
        return false;
    }

    public boolean isInherited() {
        return !infoClass().equals(definedInfoClass());
    }

    public InvokedMethod getParentMethod() {
        if (isInherited()) {
            return _invokedMethods.iterator().next();
        }
        throw new InstrumentationException(
                "getParentMethod() should not be called on a method where isInherited() is false");
    }

    public boolean isLeaf() {
        // Mtodo  leaf se no invocar mtodos ou se s se invocar a si mesmo recursivamente
        if (_invokedMethods.size() == 0)
            return true;
        if (_invokedMethods.size() == 1) {
            InvokedMethod m = _invokedMethods.iterator().next();
            return (m.owner().equals(infoClass().type()) && m.name().equals(name()) && m.desc().equals(desc()));
        }
        return false;
    }

    public List<InfoMethod> subclassOverrides(Map<Type, InfoClass> reachableMap) {
        // Constructores no podem ser overridden
        if (name().equals("<init>"))
            return new ArrayList<InfoMethod>();
        // Mtodos finais no podem ser overridden
        if (isFinal())
            return new ArrayList<InfoMethod>();

        NavigableSet<InfoClass> processingSet = new TreeSet<InfoClass>(infoClass().subclasses());
        List<InfoMethod> lst = new ArrayList<InfoMethod>();
        while (!processingSet.isEmpty()) {
            InfoClass ic = processingSet.pollFirst();
            if ((reachableMap == null) || reachableMap.containsKey(ic.type())) {
                InfoMethod m = ic.getMethod(name(), desc());
                if (m != null)
                    lst.add(m);
                processingSet.addAll(ic.subclasses());
            }
        }
        return lst;
    }

    public Type returnType() {
        return Type.fromBytecode(desc().substring(desc().indexOf(")") + 1));
    }

    public Type genericReturnType() {
        if (signature() == null)
            return null;
        return Type.fromBytecode(signature().substring(signature().indexOf(")") + 1));
    }

    public UtilList<Type> argumentTypes() {
        return Type.getArgumentTypes(desc());
    }

    public UtilList<Type> genericArgumentTypes() {
        if (signature() == null)
            return null;
        return Type.getArgumentTypes(signature());
    }

    public String genericConstraints() {
        if (signature() == null)
            return null;
        return signature().substring(0, signature().indexOf("("));
    }

    public String javaDesc() {
        String output = " (";
        StringList sl = new StringList();
        for (Type t : argumentTypes()) {
            sl.add(t.isPrimitive() ? t.primitiveTypeName() : t.commonName());
        }
        output += sl.join(", ") + ")";
        return output;
    }

    public boolean isStatic() {
        return (access() & Opcodes.ACC_STATIC) != 0;
    }

    public boolean isCtor() {
        return (name().equals("<init>"));
    }

    public boolean isStaticCtor() {
        return (name().equals("<clinit>"));
    }

}