• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.code_intelligence.jazzer.autofuzz;
16 
17 import com.code_intelligence.jazzer.api.AutofuzzConstructionException;
18 import com.code_intelligence.jazzer.api.AutofuzzInvocationException;
19 import com.code_intelligence.jazzer.api.Consumer1;
20 import com.code_intelligence.jazzer.api.Consumer2;
21 import com.code_intelligence.jazzer.api.Consumer3;
22 import com.code_intelligence.jazzer.api.Consumer4;
23 import com.code_intelligence.jazzer.api.Consumer5;
24 import com.code_intelligence.jazzer.api.Function1;
25 import com.code_intelligence.jazzer.api.Function2;
26 import com.code_intelligence.jazzer.api.Function3;
27 import com.code_intelligence.jazzer.api.Function4;
28 import com.code_intelligence.jazzer.api.Function5;
29 import com.code_intelligence.jazzer.api.FuzzedDataProvider;
30 import com.code_intelligence.jazzer.utils.Utils;
31 import io.github.classgraph.ClassGraph;
32 import io.github.classgraph.ClassInfoList;
33 import io.github.classgraph.ScanResult;
34 import java.io.ByteArrayInputStream;
35 import java.io.InputStream;
36 import java.lang.reflect.Array;
37 import java.lang.reflect.Constructor;
38 import java.lang.reflect.Executable;
39 import java.lang.reflect.GenericArrayType;
40 import java.lang.reflect.InvocationTargetException;
41 import java.lang.reflect.Method;
42 import java.lang.reflect.Modifier;
43 import java.lang.reflect.ParameterizedType;
44 import java.lang.reflect.Type;
45 import java.lang.reflect.TypeVariable;
46 import java.lang.reflect.WildcardType;
47 import java.util.*;
48 import java.util.stream.Collectors;
49 import java.util.stream.IntStream;
50 import net.jodah.typetools.TypeResolver;
51 import net.jodah.typetools.TypeResolver.Unknown;
52 
53 public class Meta {
54   static final WeakHashMap<Class<?>, List<Class<?>>> implementingClassesCache = new WeakHashMap<>();
55   static final WeakHashMap<Class<?>, List<Class<?>>> nestedBuilderClassesCache =
56       new WeakHashMap<>();
57   static final WeakHashMap<Class<?>, List<Method>> originalObjectCreationMethodsCache =
58       new WeakHashMap<>();
59   static final WeakHashMap<Class<?>, List<Method>> cascadingBuilderMethodsCache =
60       new WeakHashMap<>();
61 
autofuzz(FuzzedDataProvider data, Method method)62   public static Object autofuzz(FuzzedDataProvider data, Method method) {
63     return autofuzz(data, method, null);
64   }
65 
autofuzz(FuzzedDataProvider data, Method method, AutofuzzCodegenVisitor visitor)66   static Object autofuzz(FuzzedDataProvider data, Method method, AutofuzzCodegenVisitor visitor) {
67     Object result;
68     if (Modifier.isStatic(method.getModifiers())) {
69       if (visitor != null) {
70         // This group will always have two elements: The class name and the method call.
71         visitor.pushGroup(
72             String.format("%s.", method.getDeclaringClass().getCanonicalName()), "", "");
73       }
74       try {
75         result = autofuzz(data, method, null, visitor);
76       } finally {
77         if (visitor != null) {
78           visitor.popGroup();
79         }
80       }
81     } else {
82       if (visitor != null) {
83         // This group will always have two elements: The thisObject and the method call.
84         // Since the this object can be a complex expression, wrap it in paranthesis.
85         visitor.pushGroup("(", ").", "");
86       }
87       Object thisObject = consume(data, method.getDeclaringClass(), visitor);
88       if (thisObject == null) {
89         throw new AutofuzzConstructionException();
90       }
91       try {
92         result = autofuzz(data, method, thisObject, visitor);
93       } finally {
94         if (visitor != null) {
95           visitor.popGroup();
96         }
97       }
98     }
99     return result;
100   }
101 
autofuzz(FuzzedDataProvider data, Method method, Object thisObject)102   public static Object autofuzz(FuzzedDataProvider data, Method method, Object thisObject) {
103     return autofuzz(data, method, thisObject, null);
104   }
105 
autofuzz( FuzzedDataProvider data, Method method, Object thisObject, AutofuzzCodegenVisitor visitor)106   static Object autofuzz(
107       FuzzedDataProvider data, Method method, Object thisObject, AutofuzzCodegenVisitor visitor) {
108     if (visitor != null) {
109       visitor.pushGroup(String.format("%s(", method.getName()), ", ", ")");
110     }
111     Object[] arguments = consumeArguments(data, method, visitor);
112     if (visitor != null) {
113       visitor.popGroup();
114     }
115     try {
116       return method.invoke(thisObject, arguments);
117     } catch (IllegalAccessException | IllegalArgumentException | NullPointerException e) {
118       // We should ensure that the arguments fed into the method are always valid.
119       throw new AutofuzzError(getDebugSummary(method, thisObject, arguments), e);
120     } catch (InvocationTargetException e) {
121       throw new AutofuzzInvocationException(e.getCause());
122     }
123   }
124 
autofuzz(FuzzedDataProvider data, Constructor<R> constructor)125   public static <R> R autofuzz(FuzzedDataProvider data, Constructor<R> constructor) {
126     return autofuzz(data, constructor, null);
127   }
128 
autofuzz( FuzzedDataProvider data, Constructor<R> constructor, AutofuzzCodegenVisitor visitor)129   static <R> R autofuzz(
130       FuzzedDataProvider data, Constructor<R> constructor, AutofuzzCodegenVisitor visitor) {
131     if (visitor != null) {
132       // getCanonicalName is correct also for nested classes.
133       visitor.pushGroup(
134           String.format("new %s(", constructor.getDeclaringClass().getCanonicalName()), ", ", ")");
135     }
136     Object[] arguments = consumeArguments(data, constructor, visitor);
137     if (visitor != null) {
138       visitor.popGroup();
139     }
140     try {
141       return constructor.newInstance(arguments);
142     } catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) {
143       // This should never be reached as the logic in consume should prevent us from e.g. calling
144       // constructors of abstract classes or private constructors.
145       throw new AutofuzzError(getDebugSummary(constructor, null, arguments), e);
146     } catch (InvocationTargetException e) {
147       throw new AutofuzzInvocationException(e.getCause());
148     }
149   }
150 
151   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Consumer1<T1> func)152   public static <T1> void autofuzz(FuzzedDataProvider data, Consumer1<T1> func) {
153     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer1.class, func.getClass());
154     func.accept((T1) consumeChecked(data, types, 0));
155   }
156 
157   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Consumer2<T1, T2> func)158   public static <T1, T2> void autofuzz(FuzzedDataProvider data, Consumer2<T1, T2> func) {
159     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer2.class, func.getClass());
160     func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1));
161   }
162 
163   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Consumer3<T1, T2, T3> func)164   public static <T1, T2, T3> void autofuzz(FuzzedDataProvider data, Consumer3<T1, T2, T3> func) {
165     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer3.class, func.getClass());
166     func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
167         (T3) consumeChecked(data, types, 2));
168   }
169 
170   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Consumer4<T1, T2, T3, T4> func)171   public static <T1, T2, T3, T4> void autofuzz(
172       FuzzedDataProvider data, Consumer4<T1, T2, T3, T4> func) {
173     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer4.class, func.getClass());
174     func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
175         (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3));
176   }
177 
178   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Consumer5<T1, T2, T3, T4, T5> func)179   public static <T1, T2, T3, T4, T5> void autofuzz(
180       FuzzedDataProvider data, Consumer5<T1, T2, T3, T4, T5> func) {
181     Class<?>[] types = TypeResolver.resolveRawArguments(Consumer5.class, func.getClass());
182     func.accept((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
183         (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3),
184         (T5) consumeChecked(data, types, 4));
185   }
186 
187   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Function1<T1, R> func)188   public static <T1, R> R autofuzz(FuzzedDataProvider data, Function1<T1, R> func) {
189     Class<?>[] types = TypeResolver.resolveRawArguments(Function1.class, func.getClass());
190     return func.apply((T1) consumeChecked(data, types, 0));
191   }
192 
193   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Function2<T1, T2, R> func)194   public static <T1, T2, R> R autofuzz(FuzzedDataProvider data, Function2<T1, T2, R> func) {
195     Class<?>[] types = TypeResolver.resolveRawArguments(Function2.class, func.getClass());
196     return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1));
197   }
198 
199   @SuppressWarnings("unchecked")
autofuzz(FuzzedDataProvider data, Function3<T1, T2, T3, R> func)200   public static <T1, T2, T3, R> R autofuzz(FuzzedDataProvider data, Function3<T1, T2, T3, R> func) {
201     Class<?>[] types = TypeResolver.resolveRawArguments(Function3.class, func.getClass());
202     return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
203         (T3) consumeChecked(data, types, 2));
204   }
205 
206   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Function4<T1, T2, T3, T4, R> func)207   public static <T1, T2, T3, T4, R> R autofuzz(
208       FuzzedDataProvider data, Function4<T1, T2, T3, T4, R> func) {
209     Class<?>[] types = TypeResolver.resolveRawArguments(Function4.class, func.getClass());
210     return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
211         (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3));
212   }
213 
214   @SuppressWarnings("unchecked")
autofuzz( FuzzedDataProvider data, Function5<T1, T2, T3, T4, T5, R> func)215   public static <T1, T2, T3, T4, T5, R> R autofuzz(
216       FuzzedDataProvider data, Function5<T1, T2, T3, T4, T5, R> func) {
217     Class<?>[] types = TypeResolver.resolveRawArguments(Function5.class, func.getClass());
218     return func.apply((T1) consumeChecked(data, types, 0), (T2) consumeChecked(data, types, 1),
219         (T3) consumeChecked(data, types, 2), (T4) consumeChecked(data, types, 3),
220         (T5) consumeChecked(data, types, 4));
221   }
222 
consume(FuzzedDataProvider data, Class<?> type)223   public static Object consume(FuzzedDataProvider data, Class<?> type) {
224     return consume(data, type, null);
225   }
226 
227   // Invariant: The Java source code representation of the returned object visited by visitor must
228   // represent an object of the same type as genericType. For example, a null value returned for
229   // the genericType Class<java.lang.String> should lead to the generated code
230   // "(java.lang.String) null", not just "null". This makes it possible to safely use consume in
231   // recursive argument constructions.
consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor visitor)232   static Object consume(FuzzedDataProvider data, Type genericType, AutofuzzCodegenVisitor visitor) {
233     Class<?> type = getRawType(genericType);
234     if (type == byte.class || type == Byte.class) {
235       byte result = data.consumeByte();
236       if (visitor != null)
237         visitor.pushElement(String.format("(byte) %s", result));
238       return result;
239     } else if (type == short.class || type == Short.class) {
240       short result = data.consumeShort();
241       if (visitor != null)
242         visitor.pushElement(String.format("(short) %s", result));
243       return result;
244     } else if (type == int.class || type == Integer.class) {
245       int result = data.consumeInt();
246       if (visitor != null)
247         visitor.pushElement(Integer.toString(result));
248       return result;
249     } else if (type == long.class || type == Long.class) {
250       long result = data.consumeLong();
251       if (visitor != null)
252         visitor.pushElement(String.format("%sL", result));
253       return result;
254     } else if (type == float.class || type == Float.class) {
255       float result = data.consumeFloat();
256       if (visitor != null)
257         visitor.pushElement(String.format("%sF", result));
258       return result;
259     } else if (type == double.class || type == Double.class) {
260       double result = data.consumeDouble();
261       if (visitor != null)
262         visitor.pushElement(Double.toString(result));
263       return result;
264     } else if (type == boolean.class || type == Boolean.class) {
265       boolean result = data.consumeBoolean();
266       if (visitor != null)
267         visitor.pushElement(Boolean.toString(result));
268       return result;
269     } else if (type == char.class || type == Character.class) {
270       char result = data.consumeChar();
271       if (visitor != null)
272         visitor.addCharLiteral(result);
273       return result;
274     }
275     // Sometimes, but rarely return null for non-primitive and non-boxed types.
276     // TODO: We might want to return null for boxed types sometimes, but this is complicated by the
277     //       fact that TypeUtils can't distinguish between a primitive type and its wrapper and may
278     //       thus easily cause false-positive NullPointerExceptions.
279     if (!type.isPrimitive() && data.consumeByte() == 0) {
280       if (visitor != null) {
281         if (type == Object.class) {
282           visitor.pushElement("null");
283         } else {
284           visitor.pushElement(String.format("(%s) null", type.getCanonicalName()));
285         }
286       }
287       return null;
288     }
289     if (type == String.class || type == CharSequence.class) {
290       String result = data.consumeString(consumeArrayLength(data, 1));
291       if (visitor != null)
292         visitor.addStringLiteral(result);
293       return result;
294     } else if (type.isArray()) {
295       if (type == byte[].class) {
296         byte[] result = data.consumeBytes(consumeArrayLength(data, Byte.BYTES));
297         if (visitor != null) {
298           visitor.pushElement(IntStream.range(0, result.length)
299                                   .mapToObj(i -> "(byte) " + result[i])
300                                   .collect(Collectors.joining(", ", "new byte[]{", "}")));
301         }
302         return result;
303       } else if (type == int[].class) {
304         int[] result = data.consumeInts(consumeArrayLength(data, Integer.BYTES));
305         if (visitor != null) {
306           visitor.pushElement(Arrays.stream(result)
307                                   .mapToObj(String::valueOf)
308                                   .collect(Collectors.joining(", ", "new int[]{", "}")));
309         }
310         return result;
311       } else if (type == short[].class) {
312         short[] result = data.consumeShorts(consumeArrayLength(data, Short.BYTES));
313         if (visitor != null) {
314           visitor.pushElement(IntStream.range(0, result.length)
315                                   .mapToObj(i -> "(short) " + result[i])
316                                   .collect(Collectors.joining(", ", "new short[]{", "}")));
317         }
318         return result;
319       } else if (type == long[].class) {
320         long[] result = data.consumeLongs(consumeArrayLength(data, Long.BYTES));
321         if (visitor != null) {
322           visitor.pushElement(Arrays.stream(result)
323                                   .mapToObj(e -> e + "L")
324                                   .collect(Collectors.joining(", ", "new long[]{", "}")));
325         }
326         return result;
327       } else if (type == boolean[].class) {
328         boolean[] result = data.consumeBooleans(consumeArrayLength(data, 1));
329         if (visitor != null) {
330           visitor.pushElement(
331               Arrays.toString(result).replace(']', '}').replace("[", "new boolean[]{"));
332         }
333         return result;
334       } else {
335         if (visitor != null) {
336           visitor.pushGroup(
337               String.format("new %s[]{", type.getComponentType().getName()), ", ", "}");
338         }
339         int remainingBytesBeforeFirstElementCreation = data.remainingBytes();
340         Object firstElement = consume(data, type.getComponentType(), visitor);
341         int remainingBytesAfterFirstElementCreation = data.remainingBytes();
342         int sizeOfElementEstimate =
343             remainingBytesBeforeFirstElementCreation - remainingBytesAfterFirstElementCreation;
344         Object array = Array.newInstance(
345             type.getComponentType(), consumeArrayLength(data, sizeOfElementEstimate));
346         for (int i = 0; i < Array.getLength(array); i++) {
347           if (i == 0) {
348             Array.set(array, i, firstElement);
349           } else {
350             Array.set(array, i, consume(data, type.getComponentType(), visitor));
351           }
352         }
353         if (visitor != null) {
354           if (Array.getLength(array) == 0) {
355             // We implicitly pushed the first element with the call to consume above, but it is not
356             // part of the array.
357             visitor.popElement();
358           }
359           visitor.popGroup();
360         }
361         return array;
362       }
363     } else if (type == ByteArrayInputStream.class || type == InputStream.class) {
364       byte[] array = data.consumeBytes(consumeArrayLength(data, Byte.BYTES));
365       if (visitor != null) {
366         visitor.pushElement(IntStream.range(0, array.length)
367                                 .mapToObj(i -> "(byte) " + array[i])
368                                 .collect(Collectors.joining(
369                                     ", ", "new java.io.ByteArrayInputStream(new byte[]{", "})")));
370       }
371       return new ByteArrayInputStream(array);
372     } else if (type == Map.class) {
373       ParameterizedType mapType = (ParameterizedType) genericType;
374       if (mapType.getActualTypeArguments().length != 2) {
375         throw new AutofuzzError(
376             "Expected Map generic type to have two type parameters: " + mapType);
377       }
378       Type keyType = mapType.getActualTypeArguments()[0];
379       Type valueType = mapType.getActualTypeArguments()[1];
380       if (visitor != null) {
381         // Do not use Collectors.toMap() since it cannot handle null values.
382         // Also annotate the type of the entry stream since it might be empty, in which case type
383         // inference on the accumulator could fail.
384         visitor.pushGroup(
385             String.format("java.util.stream.Stream.<java.util.AbstractMap.SimpleEntry<%s, %s>>of(",
386                 keyType.getTypeName(), valueType.getTypeName()),
387             ", ",
388             ").collect(java.util.HashMap::new, (map, e) -> map.put(e.getKey(), e.getValue()), java.util.HashMap::putAll)");
389       }
390       int remainingBytesBeforeFirstEntryCreation = data.remainingBytes();
391       if (visitor != null) {
392         visitor.pushGroup("new java.util.AbstractMap.SimpleEntry<>(", ", ", ")");
393       }
394       Object firstKey = consume(data, keyType, visitor);
395       Object firstValue = consume(data, valueType, visitor);
396       if (visitor != null) {
397         visitor.popGroup();
398       }
399       int remainingBytesAfterFirstEntryCreation = data.remainingBytes();
400       int sizeOfElementEstimate =
401           remainingBytesBeforeFirstEntryCreation - remainingBytesAfterFirstEntryCreation;
402       int mapSize = consumeArrayLength(data, sizeOfElementEstimate);
403       Map<Object, Object> map = new HashMap<>(mapSize);
404       for (int i = 0; i < mapSize; i++) {
405         if (i == 0) {
406           map.put(firstKey, firstValue);
407         } else {
408           if (visitor != null) {
409             visitor.pushGroup("new java.util.AbstractMap.SimpleEntry<>(", ", ", ")");
410           }
411           map.put(consume(data, keyType, visitor), consume(data, valueType, visitor));
412           if (visitor != null) {
413             visitor.popGroup();
414           }
415         }
416       }
417       if (visitor != null) {
418         if (mapSize == 0) {
419           // We implicitly pushed the first entry with the call to consume above, but it is not
420           // part of the array.
421           visitor.popElement();
422         }
423         visitor.popGroup();
424       }
425       return map;
426     } else if (type.isEnum()) {
427       Enum<?> enumValue = (Enum<?>) data.pickValue(type.getEnumConstants());
428       if (visitor != null) {
429         visitor.pushElement(String.format("%s.%s", type.getName(), enumValue.name()));
430       }
431       return enumValue;
432     } else if (type == Class.class) {
433       if (visitor != null)
434         visitor.pushElement(String.format("%s.class", YourAverageJavaClass.class.getName()));
435       return YourAverageJavaClass.class;
436     } else if (type == Method.class) {
437       if (visitor != null) {
438         throw new AutofuzzError("codegen has not been implemented for Method.class");
439       }
440       return data.pickValue(sortExecutables(YourAverageJavaClass.class.getMethods()));
441     } else if (type == Constructor.class) {
442       if (visitor != null) {
443         throw new AutofuzzError("codegen has not been implemented for Constructor.class");
444       }
445       return data.pickValue(sortExecutables(YourAverageJavaClass.class.getConstructors()));
446     } else if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) {
447       List<Class<?>> implementingClasses = implementingClassesCache.get(type);
448       if (implementingClasses == null) {
449         ClassGraph classGraph =
450             new ClassGraph().enableClassInfo().enableInterClassDependencies().rejectPackages(
451                 "jaz.*");
452         if (!isTest()) {
453           classGraph.rejectPackages("com.code_intelligence.jazzer.*");
454         }
455         try (ScanResult result = classGraph.scan()) {
456           ClassInfoList children =
457               type.isInterface() ? result.getClassesImplementing(type) : result.getSubclasses(type);
458           implementingClasses =
459               children.getStandardClasses().filter(cls -> !cls.isAbstract()).loadClasses();
460           implementingClassesCache.put(type, implementingClasses);
461         }
462       }
463       if (implementingClasses.isEmpty()) {
464         if (isDebug()) {
465           throw new AutofuzzConstructionException(String.format(
466               "Could not find classes implementing %s on the classpath", type.getName()));
467         } else {
468           throw new AutofuzzConstructionException();
469         }
470       }
471       if (visitor != null) {
472         // This group will always have a single element: The instance of the implementing class.
473         visitor.pushGroup(String.format("(%s) ", type.getName()), "", "");
474       }
475       Object result = consume(data, data.pickValue(implementingClasses), visitor);
476       if (visitor != null) {
477         visitor.popGroup();
478       }
479       return result;
480     } else if (type.getConstructors().length > 0) {
481       Constructor<?> constructor = data.pickValue(sortExecutables(type.getConstructors()));
482       boolean applySetters = constructor.getParameterCount() == 0;
483       if (visitor != null && applySetters) {
484         // Embed the instance creation and setters into an immediately invoked lambda expression to
485         // turn them into an expression.
486         String uniqueVariableName = visitor.uniqueVariableName();
487         visitor.pushGroup(String.format("((java.util.function.Supplier<%1$s>) (() -> {%1$s %2$s = ",
488                               type.getCanonicalName(), uniqueVariableName),
489             String.format("; %s.", uniqueVariableName),
490             String.format("; return %s;})).get()", uniqueVariableName));
491       }
492       Object obj = autofuzz(data, constructor, visitor);
493       if (applySetters) {
494         List<Method> potentialSetters = getPotentialSetters(type);
495         if (!potentialSetters.isEmpty()) {
496           List<Method> pickedSetters =
497               data.pickValues(potentialSetters, data.consumeInt(0, potentialSetters.size()));
498           for (Method setter : pickedSetters) {
499             autofuzz(data, setter, obj, visitor);
500           }
501         }
502         if (visitor != null) {
503           visitor.popGroup();
504         }
505       }
506       return obj;
507     }
508     // We are out of more or less canonical ways to construct an instance of this class and have to
509     // resort to more heuristic approaches.
510 
511     // First, try to find nested classes with names ending in Builder and call a subset of their
512     // chaining methods.
513     List<Class<?>> nestedBuilderClasses = getNestedBuilderClasses(type);
514     if (!nestedBuilderClasses.isEmpty()) {
515       Class<?> pickedBuilder = data.pickValue(nestedBuilderClasses);
516       List<Method> cascadingBuilderMethods = getCascadingBuilderMethods(pickedBuilder);
517       List<Method> originalObjectCreationMethods = getOriginalObjectCreationMethods(pickedBuilder);
518 
519       int pickedMethodsNumber = data.consumeInt(0, cascadingBuilderMethods.size());
520       List<Method> pickedMethods = data.pickValues(cascadingBuilderMethods, pickedMethodsNumber);
521       Method builderMethod = data.pickValue(originalObjectCreationMethods);
522 
523       if (visitor != null) {
524         // Group for the chain of builder methods.
525         visitor.pushGroup("", ".", "");
526       }
527       Object builderObj =
528           autofuzz(data, data.pickValue(sortExecutables(pickedBuilder.getConstructors())), visitor);
529       for (Method method : pickedMethods) {
530         builderObj = autofuzz(data, method, builderObj, visitor);
531       }
532 
533       try {
534         Object obj = autofuzz(data, builderMethod, builderObj, visitor);
535         if (visitor != null) {
536           visitor.popGroup();
537         }
538         return obj;
539       } catch (Exception e) {
540         throw new AutofuzzConstructionException(e);
541       }
542     }
543 
544     // We ran out of ways to construct an instance of the requested type. If in debug mode, report
545     // more detailed information.
546     if (!isDebug()) {
547       throw new AutofuzzConstructionException();
548     } else {
549       String summary = String.format(
550           "Failed to generate instance of %s:%nAccessible constructors: %s%nNested subclasses: %s%n",
551           type.getName(),
552           Arrays.stream(type.getConstructors())
553               .map(Utils::getReadableDescriptor)
554               .collect(Collectors.joining(", ")),
555           Arrays.stream(type.getClasses()).map(Class::getName).collect(Collectors.joining(", ")));
556       throw new AutofuzzConstructionException(summary);
557     }
558   }
559 
rescanClasspath()560   static void rescanClasspath() {
561     implementingClassesCache.clear();
562   }
563 
isTest()564   static boolean isTest() {
565     String value = System.getenv("JAZZER_AUTOFUZZ_TESTING");
566     return value != null && !value.isEmpty();
567   }
568 
isDebug()569   static boolean isDebug() {
570     String value = System.getenv("JAZZER_AUTOFUZZ_DEBUG");
571     return value != null && !value.isEmpty();
572   }
573 
consumeArrayLength(FuzzedDataProvider data, int sizeOfElement)574   private static int consumeArrayLength(FuzzedDataProvider data, int sizeOfElement) {
575     // Spend at most half of the fuzzer input bytes so that the remaining arguments that require
576     // construction still have non-trivial data to work with.
577     int bytesToSpend = data.remainingBytes() / 2;
578     return bytesToSpend / Math.max(sizeOfElement, 1);
579   }
580 
getDebugSummary( Executable executable, Object thisObject, Object[] arguments)581   private static String getDebugSummary(
582       Executable executable, Object thisObject, Object[] arguments) {
583     return String.format("%nMethod: %s::%s%s%nthis: %s%nArguments: %s",
584         executable.getDeclaringClass().getName(), executable.getName(),
585         Utils.getReadableDescriptor(executable), thisObject,
586         Arrays.stream(arguments)
587             .map(arg -> arg == null ? "null" : arg.toString())
588             .collect(Collectors.joining(", ")));
589   }
590 
sortExecutables(T[] executables)591   private static <T extends Executable> List<T> sortExecutables(T[] executables) {
592     List<T> list = Arrays.asList(executables);
593     sortExecutables(list);
594     return list;
595   }
596 
sortExecutables(List<? extends Executable> executables)597   private static void sortExecutables(List<? extends Executable> executables) {
598     executables.sort(Comparator.comparing(Executable::getName).thenComparing(Utils::getDescriptor));
599   }
600 
sortClasses(List<? extends Class<?>> classes)601   private static void sortClasses(List<? extends Class<?>> classes) {
602     classes.sort(Comparator.comparing(Class::getName));
603   }
604 
getNestedBuilderClasses(Class<?> type)605   private static List<Class<?>> getNestedBuilderClasses(Class<?> type) {
606     List<Class<?>> nestedBuilderClasses = nestedBuilderClassesCache.get(type);
607     if (nestedBuilderClasses == null) {
608       nestedBuilderClasses = Arrays.stream(type.getClasses())
609                                  .filter(cls -> cls.getName().endsWith("Builder"))
610                                  .filter(cls -> !getOriginalObjectCreationMethods(cls).isEmpty())
611                                  .collect(Collectors.toList());
612       sortClasses(nestedBuilderClasses);
613       nestedBuilderClassesCache.put(type, nestedBuilderClasses);
614     }
615     return nestedBuilderClasses;
616   }
617 
getOriginalObjectCreationMethods(Class<?> builder)618   private static List<Method> getOriginalObjectCreationMethods(Class<?> builder) {
619     List<Method> originalObjectCreationMethods = originalObjectCreationMethodsCache.get(builder);
620     if (originalObjectCreationMethods == null) {
621       originalObjectCreationMethods =
622           Arrays.stream(builder.getMethods())
623               .filter(m -> m.getReturnType() == builder.getEnclosingClass())
624               .collect(Collectors.toList());
625       sortExecutables(originalObjectCreationMethods);
626       originalObjectCreationMethodsCache.put(builder, originalObjectCreationMethods);
627     }
628     return originalObjectCreationMethods;
629   }
630 
getCascadingBuilderMethods(Class<?> builder)631   private static List<Method> getCascadingBuilderMethods(Class<?> builder) {
632     List<Method> cascadingBuilderMethods = cascadingBuilderMethodsCache.get(builder);
633     if (cascadingBuilderMethods == null) {
634       cascadingBuilderMethods = Arrays.stream(builder.getMethods())
635                                     .filter(m -> m.getReturnType() == builder)
636                                     .collect(Collectors.toList());
637       sortExecutables(cascadingBuilderMethods);
638       cascadingBuilderMethodsCache.put(builder, cascadingBuilderMethods);
639     }
640     return cascadingBuilderMethods;
641   }
642 
getPotentialSetters(Class<?> type)643   private static List<Method> getPotentialSetters(Class<?> type) {
644     List<Method> potentialSetters = new ArrayList<>();
645     Method[] methods = type.getMethods();
646     for (Method method : methods) {
647       if (void.class.equals(method.getReturnType()) && method.getParameterCount() == 1
648           && method.getName().startsWith("set")) {
649         potentialSetters.add(method);
650       }
651     }
652     sortExecutables(potentialSetters);
653     return potentialSetters;
654   }
655 
consumeArguments( FuzzedDataProvider data, Executable executable, AutofuzzCodegenVisitor visitor)656   private static Object[] consumeArguments(
657       FuzzedDataProvider data, Executable executable, AutofuzzCodegenVisitor visitor) {
658     Object[] result;
659     try {
660       result = Arrays.stream(executable.getGenericParameterTypes())
661                    .map((type) -> consume(data, type, visitor))
662                    .toArray();
663       return result;
664     } catch (AutofuzzConstructionException e) {
665       // Do not nest AutofuzzConstructionExceptions.
666       throw e;
667     } catch (AutofuzzInvocationException e) {
668       // If an invocation fails while creating the arguments for another invocation, the exception
669       // should not be reported, so we rewrap it.
670       throw new AutofuzzConstructionException(e.getCause());
671     } catch (Throwable t) {
672       throw new AutofuzzConstructionException(t);
673     }
674   }
675 
consumeChecked(FuzzedDataProvider data, Class<?>[] types, int i)676   private static Object consumeChecked(FuzzedDataProvider data, Class<?>[] types, int i) {
677     if (types[i] == Unknown.class) {
678       throw new AutofuzzError("Failed to determine type of argument " + (i + 1));
679     }
680     Object result;
681     try {
682       result = consume(data, types[i]);
683     } catch (AutofuzzConstructionException e) {
684       // Do not nest AutofuzzConstructionExceptions.
685       throw e;
686     } catch (AutofuzzInvocationException e) {
687       // If an invocation fails while creating the arguments for another invocation, the exception
688       // should not be reported, so we rewrap it.
689       throw new AutofuzzConstructionException(e.getCause());
690     } catch (Throwable t) {
691       throw new AutofuzzConstructionException(t);
692     }
693     if (result != null && !types[i].isAssignableFrom(result.getClass())) {
694       throw new AutofuzzError("consume returned " + result.getClass() + ", but need " + types[i]);
695     }
696     return result;
697   }
698 
getRawType(Type genericType)699   private static Class<?> getRawType(Type genericType) {
700     if (genericType instanceof Class<?>) {
701       return (Class<?>) genericType;
702     } else if (genericType instanceof ParameterizedType) {
703       return getRawType(((ParameterizedType) genericType).getRawType());
704     } else if (genericType instanceof WildcardType) {
705       // TODO: Improve this.
706       return Object.class;
707     } else if (genericType instanceof TypeVariable<?>) {
708       throw new AutofuzzError("Did not expect genericType to be a TypeVariable: " + genericType);
709     } else if (genericType instanceof GenericArrayType) {
710       // TODO: Improve this;
711       return Object[].class;
712     } else {
713       throw new AutofuzzError("Got unexpected class implementing Type: " + genericType);
714     }
715   }
716 }
717