Skip to content

Commit 833a5f9

Browse files
committed
mutation: Add hasFixedSize() method to all mutators
This function will be used in a follow-up change to allow collection mutators to decide how "aggressive" they should be when mutating and resizing the collection.
1 parent 9e27db5 commit 833a5f9

File tree

17 files changed

+235
-9
lines changed

17 files changed

+235
-9
lines changed

src/main/java/com/code_intelligence/jazzer/mutation/api/InPlaceMutator.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,13 @@ public interface InPlaceMutator<T> extends Debuggable {
7575
* </ul>
7676
*/
7777
void crossOverInPlace(T reference, T otherReference, PseudoRandom prng);
78+
79+
/**
80+
* Whether the type {@code T} mutated by this mutator has a fixed size in memory. This information
81+
* can be used by mutators for collections of {@code T}s.
82+
*
83+
* <p>Examples of types with fixed size include primitive types, enums, and classes with only
84+
* primitive types and enums as members.
85+
*/
86+
boolean hasFixedSize();
7887
}

src/main/java/com/code_intelligence/jazzer/mutation/api/ValueMutator.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* @param <T> the type this mutator operates on
4242
*/
4343
public interface ValueMutator<T> extends Debuggable {
44+
4445
/**
4546
* Implementations
4647
*
@@ -79,4 +80,13 @@ public interface ValueMutator<T> extends Debuggable {
7980
*/
8081
@CheckReturnValue
8182
T crossOver(T value, T otherValue, PseudoRandom prng);
83+
84+
/**
85+
* Whether the type {@code T} mutated by this mutator has a fixed size in memory. This information
86+
* can be used by mutators for collections of {@code T}s.
87+
*
88+
* <p>Examples of types with fixed size include primitive types, enums, and classes with only
89+
* primitive types and enums as members.
90+
*/
91+
boolean hasFixedSize();
8292
}

src/main/java/com/code_intelligence/jazzer/mutation/combinator/MutatorCombinators.java

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) {
8787
setter.accept(reference, crossedOver);
8888
}
8989

90+
@Override
91+
public boolean hasFixedSize() {
92+
return mutator.hasFixedSize();
93+
}
94+
9095
@Override
9196
public String toDebugString(Predicate<Debuggable> isInCycle) {
9297
Class<?> owningType =
@@ -121,6 +126,11 @@ public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) {
121126
mutator.crossOverInPlace(map.apply(reference), map.apply(otherReference), prng);
122127
}
123128

129+
@Override
130+
public boolean hasFixedSize() {
131+
return mutator.hasFixedSize();
132+
}
133+
124134
@Override
125135
public String toDebugString(Predicate<Debuggable> isInCycle) {
126136
Class<?> owningType = TypeResolver.resolveRawArguments(Function.class, map.getClass())[0];
@@ -155,6 +165,11 @@ public void mutateInPlace(T reference, PseudoRandom prng) {}
155165
@Override
156166
public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) {}
157167

168+
@Override
169+
public boolean hasFixedSize() {
170+
return true;
171+
}
172+
158173
@Override
159174
public String toDebugString(Predicate<Debuggable> isInCycle) {
160175
return "{<empty>}";
@@ -167,6 +182,7 @@ public String toString() {
167182
};
168183
}
169184

185+
boolean hasFixedSize = stream(partialMutators).allMatch(InPlaceMutator::hasFixedSize);
170186
final InPlaceMutator<T>[] mutators = Arrays.copyOf(partialMutators, partialMutators.length);
171187
return new InPlaceMutator<T>() {
172188
@Override
@@ -188,6 +204,11 @@ public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) {
188204
}
189205
}
190206

207+
@Override
208+
public boolean hasFixedSize() {
209+
return hasFixedSize;
210+
}
211+
191212
@Override
192213
public String toDebugString(Predicate<Debuggable> isInCycle) {
193214
return stream(mutators)
@@ -281,6 +302,11 @@ public Integer crossOver(Integer value, Integer otherValue, PseudoRandom prng) {
281302
return prng.choice() ? value : otherValue;
282303
}
283304

305+
@Override
306+
public boolean hasFixedSize() {
307+
return true;
308+
}
309+
284310
@Override
285311
public String toDebugString(Predicate<Debuggable> isInCycle) {
286312
return "mutateIndices(" + length + ")";
@@ -309,6 +335,7 @@ public static ProductMutator mutateProduct(SerializingMutator... mutators) {
309335
@SafeVarargs
310336
public static <T> InPlaceMutator<T> mutateSumInPlace(
311337
ToIntFunction<T> getState, InPlaceMutator<T>... perStateMutators) {
338+
boolean hasFixedSize = stream(perStateMutators).allMatch(InPlaceMutator::hasFixedSize);
312339
final InPlaceMutator<T>[] mutators = Arrays.copyOf(perStateMutators, perStateMutators.length);
313340
return new InPlaceMutator<T>() {
314341
@Override
@@ -349,6 +376,11 @@ public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) {
349376
}
350377
}
351378

379+
@Override
380+
public boolean hasFixedSize() {
381+
return hasFixedSize;
382+
}
383+
352384
@Override
353385
public String toDebugString(Predicate<Debuggable> isInCycle) {
354386
return stream(mutators)
@@ -383,6 +415,11 @@ public void mutateInPlace(T reference, PseudoRandom prng) {
383415
public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) {
384416
mutator.crossOverInPlace(reference, otherReference, prng);
385417
}
418+
419+
@Override
420+
public boolean hasFixedSize() {
421+
return mutator.hasFixedSize();
422+
}
386423
};
387424
}
388425

@@ -426,6 +463,11 @@ public T mutate(T value, PseudoRandom prng) {
426463
public T crossOver(T value, T otherValue, PseudoRandom prng) {
427464
return value;
428465
}
466+
467+
@Override
468+
public boolean hasFixedSize() {
469+
return true;
470+
}
429471
};
430472
}
431473

@@ -440,32 +482,39 @@ public T crossOver(T value, T otherValue, PseudoRandom prng) {
440482
* @param serializer implementation of the {@link Serializer<T>} part
441483
* @param lazyMutator supplies the implementation of the {@link InPlaceMutator<T>} part. This is
442484
* guaranteed to be invoked exactly once and only after {@code registerSelf}.
485+
* @param hasFixedSize the value to return from the resulting mutators {@link
486+
* InPlaceMutator#hasFixedSize()}
443487
*/
444488
public static <T> SerializingInPlaceMutator<T> assemble(
445489
Consumer<SerializingInPlaceMutator<T>> registerSelf,
446490
Supplier<T> makeDefaultInstance,
447491
Serializer<T> serializer,
448-
Supplier<InPlaceMutator<T>> lazyMutator) {
492+
Supplier<InPlaceMutator<T>> lazyMutator,
493+
boolean hasFixedSize) {
449494
return new DelegatingSerializingInPlaceMutator<>(
450-
registerSelf, makeDefaultInstance, serializer, lazyMutator);
495+
registerSelf, makeDefaultInstance, serializer, lazyMutator, hasFixedSize);
451496
}
452497

453498
private static class DelegatingSerializingInPlaceMutator<T> extends SerializingInPlaceMutator<T> {
454499
private final Supplier<T> makeDefaultInstance;
455500
private final Serializer<T> serializer;
456501
private final InPlaceMutator<T> mutator;
502+
private final boolean hasFixedSize;
457503

458504
private DelegatingSerializingInPlaceMutator(
459505
Consumer<SerializingInPlaceMutator<T>> registerSelf,
460506
Supplier<T> makeDefaultInstance,
461507
Serializer<T> serializer,
462-
Supplier<InPlaceMutator<T>> lazyMutator) {
508+
Supplier<InPlaceMutator<T>> lazyMutator,
509+
boolean hasFixedSize) {
463510
requireNonNull(makeDefaultInstance);
464511
requireNonNull(serializer);
465512

466513
registerSelf.accept(this);
467514
this.makeDefaultInstance = makeDefaultInstance;
468515
this.serializer = serializer;
516+
// Set before invoking the supplier as that can result in calls to hasFixedSize().
517+
this.hasFixedSize = hasFixedSize;
469518
this.mutator = lazyMutator.get();
470519
}
471520

@@ -484,6 +533,13 @@ public void crossOverInPlace(T reference, T otherReference, PseudoRandom prng) {
484533
mutator.crossOverInPlace(reference, otherReference, prng);
485534
}
486535

536+
@Override
537+
public boolean hasFixedSize() {
538+
// This uses a fixed value rather than calling mutator.hasFixedSize() as this method is called
539+
// before the constructor has finished, which is necessary in the case of a cycle.
540+
return hasFixedSize;
541+
}
542+
487543
@Override
488544
protected T makeDefaultInstance() {
489545
return makeDefaultInstance.get();

src/main/java/com/code_intelligence/jazzer/mutation/combinator/PostComposedMutator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ public R crossOver(R value, R otherValue, PseudoRandom prng) {
6161
return map.apply(mutator.crossOver(inverse.apply(value), inverse.apply(otherValue), prng));
6262
}
6363

64+
@Override
65+
public boolean hasFixedSize() {
66+
return mutator.hasFixedSize();
67+
}
68+
6469
@Override
6570
public final R read(DataInputStream in) throws IOException {
6671
return map.apply(mutator.read(in));

src/main/java/com/code_intelligence/jazzer/mutation/combinator/ProductMutator.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.code_intelligence.jazzer.mutation.api.PseudoRandom;
2727
import com.code_intelligence.jazzer.mutation.api.SerializingInPlaceMutator;
2828
import com.code_intelligence.jazzer.mutation.api.SerializingMutator;
29+
import com.code_intelligence.jazzer.mutation.api.ValueMutator;
2930
import java.io.DataInputStream;
3031
import java.io.DataOutputStream;
3132
import java.io.IOException;
@@ -40,11 +41,13 @@ public final class ProductMutator extends SerializingInPlaceMutator<Object[]> {
4041
private static final int INVERSE_PICK_VALUE_SUPPLIER_FREQUENCY = 100;
4142

4243
private final SerializingMutator[] mutators;
44+
private final boolean hasFixedSize;
4345

4446
ProductMutator(SerializingMutator[] mutators) {
4547
requireNonNullElements(mutators);
4648
require(mutators.length > 0, "mutators must not be empty");
4749
this.mutators = Arrays.copyOf(mutators, mutators.length);
50+
this.hasFixedSize = stream(mutators).allMatch(ValueMutator::hasFixedSize);
4851
}
4952

5053
@Override
@@ -124,6 +127,11 @@ public void crossOverInPlace(Object[] reference, Object[] otherReference, Pseudo
124127
}
125128
}
126129

130+
@Override
131+
public boolean hasFixedSize() {
132+
return hasFixedSize;
133+
}
134+
127135
@Override
128136
public Object[] detach(Object[] value) {
129137
Object[] clone = new Object[mutators.length];

src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ public void crossOverInPlace(List<T> reference, List<T> otherReference, PseudoRa
146146
}
147147
}
148148

149+
@Override
150+
public boolean hasFixedSize() {
151+
return false;
152+
}
153+
149154
@Override
150155
public List<T> detach(List<T> value) {
151156
return value.stream()

src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ public void crossOverInPlace(Map<K, V> reference, Map<K, V> otherReference, Pseu
193193
}
194194
}
195195

196+
@Override
197+
public boolean hasFixedSize() {
198+
return false;
199+
}
200+
196201
@Override
197202
public Map<K, V> detach(Map<K, V> value) {
198203
return value.entrySet().stream()

src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/BooleanMutatorFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ public Boolean crossOver(Boolean value, Boolean otherValue, PseudoRandom prng) {
6666
return prng.choice() ? value : otherValue;
6767
}
6868

69+
@Override
70+
public boolean hasFixedSize() {
71+
return true;
72+
}
73+
6974
@Override
7075
public String toDebugString(Predicate<Debuggable> isInLoop) {
7176
return "Boolean";

src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/ByteArrayMutatorFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ private static byte[] overwritePart(byte[] value, byte[] otherValue, PseudoRando
219219
return value;
220220
}
221221

222+
@Override
223+
public boolean hasFixedSize() {
224+
return false;
225+
}
226+
222227
@Override
223228
public String toDebugString(Predicate<Debuggable> isInCycle) {
224229
return "byte[]";

src/main/java/com/code_intelligence/jazzer/mutation/mutator/lang/FloatingPointMutatorFactory.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,11 @@ public Float detach(Float value) {
361361
public String toDebugString(Predicate<Debuggable> isInCycle) {
362362
return "Float";
363363
}
364+
365+
@Override
366+
public boolean hasFixedSize() {
367+
return true;
368+
}
364369
}
365370

366371
static final class DoubleMutator extends SerializingMutator<Double> {
@@ -623,6 +628,11 @@ private double crossOverMantissa(double value, double otherValue) {
623628
return Double.longBitsToDouble(bitsWithOtherMantissa);
624629
}
625630

631+
@Override
632+
public boolean hasFixedSize() {
633+
return true;
634+
}
635+
626636
@Override
627637
public Double read(DataInputStream in) throws IOException {
628638
return forceInRange(in.readDouble(), minValue, maxValue, allowNaN);

0 commit comments

Comments
 (0)