/* * (C) Copyright Uwe Schindler (Generics Policeman) and others. * Parts of this work are licensed to the Apache Software Foundation (ASF) * under one or more contributor license agreements. * * 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 de.thetaphi.forbiddenapis; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.Method; /** Utility class that is used to get an overview of all fields and implemented * methods of a class. It make the signatures available as Sets. */ final class ClassSignature implements Constants { private ClassReader reader; public final boolean isRuntimeClass; public final Set<Method> methods; public final Set<String> fields, signaturePolymorphicMethods; public final String className, superName; public final String[] interfaces; /** Builds the information from an ASM ClassReader */ public ClassSignature(final ClassReader classReader, boolean isRuntimeClass, boolean withReader) { this.reader = withReader ? classReader : null; this.isRuntimeClass = isRuntimeClass; this.className = classReader.getClassName(); this.superName = classReader.getSuperName(); this.interfaces = classReader.getInterfaces(); final Set<Method> methods = new HashSet<Method>(); final Set<String> fields = new HashSet<String>(); final Set<String> signaturePolymorphicMethods = new HashSet<String>(); classReader.accept(new ClassVisitor(Opcodes.ASM5) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { final Method m = new Method(name, desc); methods.add(m); if (className.startsWith(SIGNATURE_POLYMORPHIC_PKG_INTERNALNAME) && (access & Opcodes.ACC_VARARGS) != 0 && (access & Opcodes.ACC_NATIVE) != 0 && SIGNATURE_POLYMORPHIC_DESCRIPTOR.equals(desc) ) { signaturePolymorphicMethods.add(name); } return null; } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { fields.add(name); return null; } }, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); this.methods = createSet(methods); this.fields = createSet(fields); this.signaturePolymorphicMethods = createSet(signaturePolymorphicMethods); } /** Alternative ctor that can be used to build the information via reflection from an already loaded class. Useful for Java 9 Jigsaw. */ public ClassSignature(final Class<?> clazz, boolean isRuntimeClass) { this.reader = null; // no reader available! this.isRuntimeClass = isRuntimeClass; this.className = Type.getType(clazz).getInternalName(); final Class<?> superclazz = clazz.getSuperclass(); this.superName = superclazz == null ? null : Type.getType(superclazz).getInternalName(); final Class<?>[] interfClasses = clazz.getInterfaces(); this.interfaces = new String[interfClasses.length]; for (int i = 0; i < interfClasses.length; i++) { this.interfaces[i] = Type.getType(interfClasses[i]).getInternalName(); } final Set<Method> methods = new HashSet<Method>(); final Set<String> fields = new HashSet<String>(); final Set<String> signaturePolymorphicMethods = new HashSet<String>(); for (final java.lang.reflect.Method m : clazz.getDeclaredMethods()) { methods.add(Method.getMethod(m)); if (className.startsWith(SIGNATURE_POLYMORPHIC_PKG_INTERNALNAME) && m.isVarArgs() && (m.getModifiers() & Modifier.NATIVE) != 0 && SIGNATURE_POLYMORPHIC_DESCRIPTOR.equals(Type.getMethodDescriptor(m)) ) { signaturePolymorphicMethods.add(m.getName()); } } for (final java.lang.reflect.Constructor<?> m : clazz.getDeclaredConstructors()) { methods.add(Method.getMethod(m)); } for (final java.lang.reflect.Field f : clazz.getDeclaredFields()) { fields.add(f.getName()); } this.methods = createSet(methods); this.fields = createSet(fields); this.signaturePolymorphicMethods = createSet(signaturePolymorphicMethods); } private static <T> Set<T> createSet(Set<? extends T> s) { return s.isEmpty() ? Collections.<T>emptySet() : Collections.<T>unmodifiableSet(s); } public ClassReader getReader() { if (reader == null) throw new IllegalStateException("'" + Type.getObjectType(className).getClassName() + "' has no ClassReader, because it was already checked or is only loaded as related class."); try { return reader; } finally { reader = null; } } }