All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.yawk.valda.ir.SuperMemberScanner Maven / Gradle / Ivy

The newest version!
package at.yawk.valda.ir;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.Value;

/**
 * This class assists in finding members (methods and fields) that a given member overrides.
 *
 * @author yawkat
 */
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class SuperMemberScanner {
    /*
     * This class is pretty complicated because of the intricacies of package-private member overrides. See the tests
     * for examples.
     */

    private final boolean methods;

    private final Set visitedTypes = new HashSet<>();
    private final Map> possibleMembers = new HashMap<>();
    /**
     * Table of all possible members on a type, including possibly-inherited members.
     */
    private final Table memberTable = HashBasedTable.create();

    protected abstract Iterable declared(LocalClassMirror classMirror);

    protected abstract void getOrCreate(TypeMirror classMirror, MemberSignature signature, boolean isStatic);

    public static SuperMemberScanner fields() {
        return new SuperMemberScanner(false) {
            @Override
            protected Iterable declared(LocalClassMirror classMirror) {
                return classMirror.getDeclaredFields();
            }

            @Override
            protected void getOrCreate(TypeMirror classMirror, MemberSignature signature, boolean isStatic) {
                classMirror.field(signature.getName(), signature.getType(), TriState.valueOf(isStatic));
            }
        };
    }

    public static SuperMemberScanner methods() {
        return new SuperMemberScanner(true) {
            @Override
            protected Iterable declared(LocalClassMirror classMirror) {
                return classMirror.getDeclaredMethods();
            }

            @Override
            protected void getOrCreate(TypeMirror classMirror, MemberSignature signature, boolean isStatic) {
                classMirror.method(signature, TriState.valueOf(isStatic));
            }
        };
    }

    private void visitType(TypeMirror type) {
        // we visit the whole type at a time (not individual members) so we do not have to scan declared() each time

        if (!(type instanceof LocalClassMirror)) { return; }

        LocalClassMirror lcm = (LocalClassMirror) type;
        if (!visitedTypes.add(lcm)) { return; }

        TypeMirror superType = lcm.getSuperType();
        if (superType != null) {
            visitType(superType);
        }
        for (TypeMirror itf : lcm.getInterfaces()) {
            visitType(itf);
        }
        Set possibleMembers = new HashSet<>();
        for (M member : declared(lcm)) {
            visitDeclared(member);
            possibleMembers.add(new PossibleMember(member.getSignature(), member.isStatic()));
        }

        if (superType instanceof LocalClassMirror) {
            possibleMembers.addAll(findAllMembers((LocalClassMirror) superType));
        }
        for (TypeMirror itf : lcm.getInterfaces()) {
            if (itf instanceof LocalClassMirror) {
                for (PossibleMember possibleMember : findAllMembers((LocalClassMirror) itf)) {
                    if (methods && possibleMember.isStatic) { continue; }

                    possibleMembers.add(possibleMember);
                }
            }
        }
        this.possibleMembers.put(lcm, possibleMembers);
    }

    private boolean isInvokeSpecial(MemberSignature signature) {
        return methods && (signature.getName().equals("") ||
                           signature.getName().equals(""));
    }

    private void visitDeclared(M member) {
        Access access = member.getAccess();
        MemberSignature signature = member.getSignature();
        PossibleMember id = new PossibleMember(signature, member.isStatic());

        if (isInvokeSpecial(signature)) { return; }

        List possibleSuperDefs;
        if (methods) {
            possibleSuperDefs = findPossibleSuperDefs(member.getDeclaringType(), id, access);
        } else {
            possibleSuperDefs = Collections.emptyList();
        }

        memberTable.put(member.getDeclaringType(), id, new Entry(possibleSuperDefs, access, true));
    }

    /**
     * Find the {@link Entry} associated with the given interface method.
     */
    private Entry findOrCreateOnInterface(TypeMirror type, PossibleMember id) {
        Entry present = memberTable.get(type, id);
        if (present == null) {
            if (type instanceof LocalClassMirror) {
                List possibleSuperDefs = new ArrayList<>();
                for (TypeMirror itf : ((LocalClassMirror) type).getInterfaces()) {
                    if (findOrCreateOnInterface(itf, id).isPresent()) {
                        possibleSuperDefs.add(itf);
                    }
                }
                // this is always java.lang.Object. support methods like toString on interfaces
                TypeMirror superType = ((LocalClassMirror) type).getSuperType();
                if (findAdHocMember(superType, id).isPresent()) {
                    possibleSuperDefs.add(superType);
                }
                if (possibleSuperDefs.isEmpty()) {
                    present = Entry.DEFINITELY_MISSING;
                } else {
                    present = new Entry(possibleSuperDefs, Access.PUBLIC, false);
                }
            } else {
                present = makeExternalEntry(type, id);
            }
            memberTable.put(type, id, present);
        }
        return present;
    }

    private Entry findAdHocMember(TypeMirror type, PossibleMember id) {
        if (isInvokeSpecial(id.signature)) { return Entry.CONSTRUCTOR; }
        Entry present = memberTable.get(type, id);
        if (present == null) {
            if (type instanceof LocalClassMirror) {
                List possibleSuperDefs = findPossibleSuperDefs((LocalClassMirror) type, id, Access.PUBLIC);
                if (possibleSuperDefs.isEmpty()) {
                    present = Entry.DEFINITELY_MISSING;
                } else {
                    present = new Entry(possibleSuperDefs, Access.PUBLIC, false);
                }
            } else {
                present = makeExternalEntry(type, id);
            }
            memberTable.put(type, id, present);
        }
        return present;
    }

    /**
     * Compute all non-interface supertypes that may declare the given method, and all interface
     * supertypes that may have the given method (but not necessarily declare them). Will return at most one
     * declaring type in an inheritance chain.
     */
    private List findPossibleSuperDefs(
            LocalClassMirror type, PossibleMember id, Access access
    ) {
        List possibleSuperDefs = new ArrayList<>();

        if (access == Access.PUBLIC) {
            findInInterfaces(possibleSuperDefs, type, id);
        }

        boolean lookingForPublic = access == Access.PUBLIC || access == Access.PROTECTED;
        boolean lookingForPackage = access != Access.PRIVATE;

        String pkg = Types.getPackage(type.getType());
        TypeMirror superType = type;
        while (lookingForPublic || lookingForPackage) {
            // for fields and static methods, we only override at most one member.
            if ((!methods || id.isStatic) && !possibleSuperDefs.isEmpty()) {
                break;
            }

            superType = ((LocalClassMirror) superType).getSuperType();
            if (superType == null) { break; }

            Entry superEntry = memberTable.get(superType, id);

            if (superType instanceof LocalClassMirror) {
                if (superEntry != null && !superEntry.declared) { superEntry = null; }

                if (superEntry != null) {
                    boolean samePkg = Types.getPackage(((LocalClassMirror) superType).getName()).equals(pkg);

                    lookingForPublic = false;
                    if (samePkg) { lookingForPackage = false; }

                    if (superEntry.access == Access.PRIVATE) {
                        continue;
                    }

                    if (superEntry.access == Access.DEFAULT && !samePkg) {
                        continue;
                    }

                    possibleSuperDefs.add(superType);
                } else {
                    if (lookingForPublic && access == Access.PUBLIC) {
                        findInInterfaces(possibleSuperDefs, (LocalClassMirror) superType, id);
                    }
                }
            } else {
                if (superEntry == null) {
                    superEntry = makeExternalEntry(superType, id);
                    memberTable.put(superType, id, superEntry);
                }

                if (lookingForPublic && superEntry.isPresent()) {
                    possibleSuperDefs.add(superType);
                }
                break;
            }
        }

        // no duplicate entries
        assert possibleSuperDefs.size() == new HashSet<>(possibleSuperDefs).size() : possibleSuperDefs;

        return possibleSuperDefs;
    }

    private void findInInterfaces(
            List possibleSuperDefs,
            LocalClassMirror type,
            PossibleMember id
    ) {
        if (methods) {
            // static methods are not inherited from interfaces
            if (id.isStatic) { return; }
        } else {
            // interfaces don't have non-static fields
            if (!id.isStatic) { return; }
        }

        for (TypeMirror itf : type.getInterfaces()) {
            if (findOrCreateOnInterface(itf, id).isPresent()) {
                if (!possibleSuperDefs.contains(itf)) {
                    possibleSuperDefs.add(itf);
                }
            }
        }
    }

    private Entry makeExternalEntry(TypeMirror type, PossibleMember id) {
        try {
            getOrCreate(type, id.signature, id.isStatic);
            return Entry.TOP_LEVEL_DEF;
        } catch (NoSuchMemberException notFound) {
            return Entry.DEFINITELY_MISSING;
        }
    }

    /**
     * @see #findOverriddenMembers(TypeMirror, PossibleMember)
     */
    public List findOverriddenMembers(M member) {
        return findOverriddenMembers(member.getDeclaringType(),
                                     new PossibleMember(member.getSignature(),
                                                        member.isStatic()));
    }

    /**
     * Find all members that the given method may override if it exists, or {@code null} iff the given method
     * definitely does not exist.
     * 

* The returned list contains types that contain a member of the same signature that this member overrides. That * member may or may not be {@link LocalMember#isDeclared() declared}. It is guaranteed that repeated calls on * the returned types (i.e. recursively calling this method again on each returned type) will eventually yield * all types that declare a member that is overridden by the initial member. *

* Consider classes A, B extends A and C extends B. C and A declare a method {@code test()V}. Calling * {@code findOverriddenMembers(C, test()V, false)} may yield [B], [A] or [B, A]. Calling {@code * findOverriddenMembers(B, test()V, false)} will yield [A]. Calling {@code findOverriddenMembers(A, test()V, * false)} will yield []. */ @Nullable public List findOverriddenMembers( TypeMirror declaringType, PossibleMember possibleMember ) { visitType(declaringType); return findAdHocMember(declaringType, possibleMember).overriddenMembers; } /** * Find all possible members on this class. This method is not very smart and may return members that do * not exist after all (i.e. where {@link #findOverriddenMembers(TypeMirror, PossibleMember)} returns {@code * * null}). */ public Collection findAllMembers(LocalClassMirror classMirror) { visitType(classMirror); return possibleMembers.get(classMirror); } @RequiredArgsConstructor(access = AccessLevel.PRIVATE) private static final class Entry { static final Entry TOP_LEVEL_DEF = new Entry(Collections.emptyList(), null, true); static final Entry CONSTRUCTOR = TOP_LEVEL_DEF; static final Entry DEFINITELY_MISSING = new Entry(null, null, false); /** * A value of {@code null} signifies the members is definitely not present on this type. Any other value * signifies that the member may be present. The contained list is the possible supertypes that may * also contain this member. */ @Nullable private final List overriddenMembers; @Nullable private final Access access; private final boolean declared; boolean isPresent() { return overriddenMembers != null; } } @Value public static final class PossibleMember { private final MemberSignature signature; private final boolean isStatic; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy