1 /* 2 * Copyright 2023 Code Intelligence GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.code_intelligence.jazzer.mutation.mutator; 18 19 import static com.code_intelligence.jazzer.mutation.support.TypeSupport.visitAnnotatedType; 20 import static java.lang.String.format; 21 import static java.util.Arrays.stream; 22 import static java.util.stream.Collectors.joining; 23 24 import com.code_intelligence.jazzer.mutation.annotation.AppliesTo; 25 import com.code_intelligence.jazzer.mutation.api.ChainedMutatorFactory; 26 import com.code_intelligence.jazzer.mutation.api.MutatorFactory; 27 import com.code_intelligence.jazzer.mutation.mutator.collection.CollectionMutators; 28 import com.code_intelligence.jazzer.mutation.mutator.lang.LangMutators; 29 import com.code_intelligence.jazzer.mutation.mutator.proto.ProtoMutators; 30 import java.lang.annotation.Annotation; 31 import java.lang.reflect.AnnotatedType; 32 33 public final class Mutators { Mutators()34 private Mutators() {} 35 newFactory()36 public static MutatorFactory newFactory() { 37 return new ChainedMutatorFactory( 38 LangMutators.newFactory(), CollectionMutators.newFactory(), ProtoMutators.newFactory()); 39 } 40 41 /** 42 * Throws an exception if any annotation on {@code type} violates the restrictions of its 43 * {@link AppliesTo} meta-annotation. 44 */ validateAnnotationUsage(AnnotatedType type)45 public static void validateAnnotationUsage(AnnotatedType type) { 46 visitAnnotatedType(type, (clazz, annotations) -> { 47 outer: 48 for (Annotation annotation : annotations) { 49 AppliesTo appliesTo = annotation.annotationType().getAnnotation(AppliesTo.class); 50 if (appliesTo == null) { 51 continue; 52 } 53 for (Class<?> allowedClass : appliesTo.value()) { 54 if (allowedClass == clazz) { 55 continue outer; 56 } 57 } 58 for (Class<?> allowedSuperClass : appliesTo.subClassesOf()) { 59 if (allowedSuperClass.isAssignableFrom(clazz)) { 60 continue outer; 61 } 62 } 63 64 String helpText = ""; 65 if (appliesTo.value().length != 0) { 66 helpText = stream(appliesTo.value()).map(Class::getName).collect(joining(", ")); 67 } 68 if (appliesTo.subClassesOf().length != 0) { 69 if (!helpText.isEmpty()) { 70 helpText += "as well as "; 71 } 72 helpText += "subclasses of "; 73 helpText += stream(appliesTo.subClassesOf()).map(Class::getName).collect(joining(", ")); 74 } 75 // Use the simple name as our annotations live in a single package. 76 throw new IllegalArgumentException(format("%s does not apply to %s, only applies to %s", 77 annotation.annotationType().getSimpleName(), clazz.getName(), helpText)); 78 } 79 }); 80 } 81 } 82