diff options
Diffstat (limited to 'src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java')
-rw-r--r-- | src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java b/src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java new file mode 100644 index 00000000..a45a474b --- /dev/null +++ b/src/main/java/com/code_intelligence/jazzer/autofuzz/AccessibleObjectLookup.java @@ -0,0 +1,147 @@ +// Copyright 2022 Code Intelligence GmbH +// +// 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 com.code_intelligence.jazzer.autofuzz; + +import io.github.classgraph.ClassInfo; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Comparator; +import java.util.stream.Stream; + +class AccessibleObjectLookup { + private static final Comparator<Class<?>> STABLE_CLASS_COMPARATOR = + Comparator.comparing(Class::getName); + private static final Comparator<Executable> STABLE_EXECUTABLE_COMPARATOR = + Comparator.comparing(Executable::getName).thenComparing(executable -> { + if (executable instanceof Method) { + return org.objectweb.asm.Type.getMethodDescriptor((Method) executable); + } else { + return org.objectweb.asm.Type.getConstructorDescriptor((Constructor<?>) executable); + } + }); + + private final Class<?> referenceClass; + + public AccessibleObjectLookup(Class<?> referenceClass) { + this.referenceClass = referenceClass; + } + + Class<?>[] getAccessibleClasses(Class<?> type) { + return Stream.concat(Arrays.stream(type.getDeclaredClasses()), Arrays.stream(type.getClasses())) + .distinct() + .filter(this::isAccessible) + .sorted(STABLE_CLASS_COMPARATOR) + .toArray(Class<?>[] ::new); + } + + Constructor<?>[] getAccessibleConstructors(Class<?> type) { + // Neither of getDeclaredConstructors and getConstructors is a superset of the other: While + // getDeclaredConstructors returns constructors with all visibility modifiers, it does not + // return the implicit default constructor. + return Stream + .concat( + Arrays.stream(type.getDeclaredConstructors()), Arrays.stream(type.getConstructors())) + .distinct() + .filter(this::isAccessible) + .sorted(STABLE_EXECUTABLE_COMPARATOR) + .filter(constructor -> { + try { + constructor.setAccessible(true); + return true; + } catch (Exception e) { + // Can't make the constructor accessible, e.g. because it is in a standard library + // module. We can't do anything about this, so we skip the constructor. + return false; + } + }) + .toArray(Constructor<?>[] ::new); + } + + Method[] getAccessibleMethods(Class<?> type) { + return Stream.concat(Arrays.stream(type.getDeclaredMethods()), Arrays.stream(type.getMethods())) + .distinct() + .filter(this::isAccessible) + .sorted(STABLE_EXECUTABLE_COMPARATOR) + .filter(method -> { + try { + method.setAccessible(true); + return true; + } catch (Exception e) { + // Can't make the method accessible, e.g. because it is in a standard library module. We + // can't do anything about this, so we skip the method. + return false; + } + }) + .toArray(Method[] ::new); + } + + boolean isAccessible(Class<?> clazz, int modifiers) { + if (Modifier.isPublic(modifiers)) { + return true; + } + if (referenceClass == null) { + return false; + } + if (Modifier.isPrivate(modifiers)) { + return clazz.equals(referenceClass); + } + if (Modifier.isProtected(modifiers)) { + return clazz.isAssignableFrom(referenceClass); + } + // No visibility modifiers implies default visibility, which means visible in the same package. + return clazz.getPackage().equals(referenceClass.getPackage()); + } + + boolean isAccessible(ClassInfo clazz, int modifiers) { + if (Modifier.isPublic(modifiers)) { + return true; + } + if (referenceClass == null) { + return false; + } + if (Modifier.isPrivate(modifiers)) { + return clazz.getName().equals(referenceClass.getName()); + } + if (Modifier.isProtected(modifiers)) { + return isAssignableFrom(clazz, referenceClass); + } + // No visibility modifiers implies default visibility, which means visible in the same package. + return clazz.getPackageName().equals(referenceClass.getPackage().getName()); + } + + boolean isAssignableFrom(ClassInfo clazz, Class<?> potentialSubclass) { + if (potentialSubclass.getName().equals(clazz.getName())) { + return true; + } + if (potentialSubclass.equals(Object.class)) { + return clazz.getName().equals(Object.class.getName()); + } + if (potentialSubclass.getSuperclass() == null) { + return false; + } + return isAssignableFrom(clazz, potentialSubclass.getSuperclass()); + } + + private boolean isAccessible(Executable executable) { + return isAccessible(executable.getDeclaringClass(), executable.getModifiers()); + } + + private boolean isAccessible(Class<?> clazz) { + return isAccessible(clazz, clazz.getModifiers()); + } +} |