ASM judges a class and implements the specified interface

Keywords: Programming Java

Technical support

In ASM, ClassReader class: resolves the existing and provides methods for obtaining class information.

Through ClassReader, we can parse a class.

Processing flow

Determine whether a class implements an interface according to the following process:

  • Loop to determine whether the target interface is achieved
  • Recursively determine whether the parent interface has a target interface
  • Recursively determine whether the parent class implements the target interface

Concrete realization

The specific implementation code is as follows:

package utils;

import org.objectweb.asm.ClassReader;

import java.io.IOException;
import java.util.Set;

/**
 * Determine whether a class implements the specified interface set
 *
 * @author pengpj
 * @date 2018/11/27
 */
public class SpecifiedInterfaceImplementionChecked {

    /**
     * Determine whether the specified interface is implemented
     *
     * @param reader       class reader
     * @param interfaceSet interface collection
     * @return check result
     */
    public static boolean hasImplSpecifiedInterfaces(ClassReader reader, Set<String> interfaceSet) {
        if (isObject(reader.getClassName())) {
            return false;
        }
        try {
            if (containedTargetInterface(reader.getInterfaces(), interfaceSet)) {
                return true;
            } else {
                ClassReader parent = new ClassReader(reader.getSuperName());
                return hasImplSpecifiedInterfaces(parent, interfaceSet);
            }
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * Check that the current class is of Object type
     *
     * @param className class name
     * @return checked result
     */
    private static boolean isObject(String className) {
        return "java/lang/Object".equals(className);
    }

    /**
     * Check whether the interface and its parent interface implement the target interface
     *
     * @param interfaceList Interface to be checked
     * @param interfaceSet  Target interface
     * @return checked result
     * @throws IOException exp
     */
    private static boolean containedTargetInterface(String[] interfaceList, Set<String> interfaceSet) throws IOException {
        for (String inter : interfaceList) {
            if (interfaceSet.contains(inter)) {
                return true;
            } else {
                ClassReader reader = new ClassReader(inter);
                if (containedTargetInterface(reader.getInterfaces(), interfaceSet)) {
                    return true;
                }
            }
        }
        return false;
    }

}

test case

Some test cases are as follows:

 private static Set<String> set;

    static {
        if (set == null) {
            set = new HashSet<>();
        }
        set.add("com/cvte/myou/apm/agent/utils/SpecifiedInterfaceImplCheckedTest$A");
    }

    @Test
    public void hasImplSpecifiedInterfacesTest() throws IOException {
        Assert.assertEquals(true, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(B.class.getName()), set));
        Assert.assertEquals(true, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(C.class.getName()), set));
    }

    @Test
    public void hasImplSpecifiedInterfacesObjectTest() throws IOException {
        Assert.assertEquals(false, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(Object.class.getName()), set));
    }

    @Test
    public void hasImplSpecifiedInterfacesInterfaceTest() throws IOException {
        Assert.assertEquals(true, SpecifiedInterfaceImplChecked.hasImplSpecifiedInterfaces(new ClassReader(D.class.getName()), set));
    }

    interface A {
        void hello();
    }

    class B implements A {
        @Override
        public void hello() {
        }
    }

    abstract class C implements A {
    }

    interface D extends A {
    }

summary

In ASM, ClassReader is provided to parse the existing classes, which enables us to operate on the classes without source code.

Posted by sebmaurer on Wed, 04 Dec 2019 08:22:06 -0800