1// Copyright 2020 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * @fileoverview Mutator for array expressions. 7 */ 8 9'use strict'; 10 11const babelTypes = require('@babel/types'); 12 13const common = require('./common.js'); 14const mutator = require('./mutator.js'); 15const random = require('../random.js'); 16 17// Blueprint for choosing the maximum number of mutations. Bias towards 18// performing only one mutation. 19const MUTATION_CHOICES = [1, 1, 1, 1, 1, 2, 2, 2, 3]; 20 21const MAX_ARRAY_LENGTH = 50; 22 23class ArrayMutator extends mutator.Mutator { 24 constructor(settings) { 25 super(); 26 this.settings = settings; 27 } 28 29 get visitor() { 30 const thisMutator = this; 31 32 return { 33 ArrayExpression(path) { 34 const elements = path.node.elements; 35 if (!random.choose(thisMutator.settings.MUTATE_ARRAYS) || 36 elements.length > MAX_ARRAY_LENGTH) { 37 return; 38 } 39 40 // Annotate array expression with the action taken, indicating 41 // if we also replaced elements. 42 function annotate(message, replace) { 43 if (replace) message += ' (replaced)'; 44 thisMutator.annotate(path.node, message); 45 } 46 47 // Add or replace elements at a random index. 48 function randomSplice(replace, ...args) { 49 // Choose an index that's small enough to replace all desired items. 50 const index = random.randInt(0, elements.length - replace); 51 elements.splice(index, replace, ...args); 52 } 53 54 function duplicateElement(replace) { 55 const element = random.single(elements); 56 if (!element || common.isLargeNode(element)) { 57 return; 58 } 59 annotate('Duplicate an element', replace); 60 randomSplice(replace, babelTypes.cloneDeep(element)); 61 } 62 63 function insertRandomValue(replace) { 64 annotate('Insert a random value', replace); 65 randomSplice(replace, common.randomValue(path)); 66 } 67 68 function insertHole(replace) { 69 annotate('Insert a hole', replace); 70 randomSplice(replace, null); 71 } 72 73 function removeElements(count) { 74 annotate('Remove elements'); 75 randomSplice(random.randInt(1, count)); 76 } 77 78 function shuffle() { 79 annotate('Shuffle array'); 80 random.shuffle(elements); 81 } 82 83 // Mutation options. Repeated mutations have a higher probability. 84 const mutations = [ 85 () => duplicateElement(1), 86 () => duplicateElement(1), 87 () => duplicateElement(1), 88 () => duplicateElement(0), 89 () => duplicateElement(0), 90 () => insertRandomValue(1), 91 () => insertRandomValue(1), 92 () => insertRandomValue(0), 93 () => insertHole(1), 94 () => insertHole(0), 95 () => removeElements(1), 96 () => removeElements(elements.length), 97 shuffle, 98 ]; 99 100 // Perform several mutations. 101 const count = random.single(MUTATION_CHOICES); 102 for (let i = 0; i < count; i++) { 103 random.single(mutations)(); 104 } 105 106 // Don't recurse on nested arrays. 107 path.skip(); 108 }, 109 } 110 } 111} 112 113module.exports = { 114 ArrayMutator: ArrayMutator, 115}; 116